Django - вход по электронной почте


86

Я хочу, чтобы django аутентифицировал пользователей по электронной почте, а не по именам пользователей. Один из способов - указать адрес электронной почты в качестве значения имени пользователя, но я этого не хочу. Причина в том, что у меня есть URL-адрес /profile/<username>/, поэтому у меня не может быть URL-адреса /profile/abcd@gmail.com/.

Другая причина в том, что все электронные письма уникальны, но иногда бывает, что имя пользователя уже используется. Следовательно, я автоматически создаю имя пользователя как fullName_ID.

Как я могу просто изменить, чтобы Django аутентифицировался по электронной почте?

Вот как я создаю пользователя.

username = `abcd28`
user_email = `abcd@gmail.com`
user = User.objects.create_user(username, user_email, user_pass)

Вот как я вхожу в систему.

email = request.POST['email']
password = request.POST['password']
username = User.objects.get(email=email.lower()).username
user = authenticate(username=username, password=password)
login(request, user)

Есть ли какой-либо другой способ входа в систему, кроме получения имени пользователя?

Ответы:


109

Вам следует написать собственный сервер аутентификации. Примерно так будет работать:

from django.contrib.auth import get_user_model
from django.contrib.auth.backends import ModelBackend

class EmailBackend(ModelBackend):
    def authenticate(self, request, username=None, password=None, **kwargs):
        UserModel = get_user_model()
        try:
            user = UserModel.objects.get(email=username)
        except UserModel.DoesNotExist:
            return None
        else:
            if user.check_password(password):
                return user
        return None

Затем установите этот бэкэнд в качестве бэкэнда аутентификации в своих настройках:

AUTHENTICATION_BACKENDS = ['path.to.auth.module.EmailBackend']

Обновлено . Наследовать от, ModelBackendпоскольку он реализует такие методы, как get_user()уже.

См. Документы здесь: https://docs.djangoproject.com/en/3.0/topics/auth/customizing/#writing-an-authentication-backend


6
При использовании django 1.9.8 у меня возникла ошибка: объект «EmailBackend» не имеет атрибута «get_user». Решено добавлением метода get_user в соответствии с этим stackoverflow.com/a/13954358/2647009
baltasvejas

1
Укажите, для какой версии Django этот код может работать. Некоторые жалуются на отсутствие метода get_user.
Доктор Юнес Хенни

2
Вместо того , чтобы if user.check_password(password):вы , вероятно , хотите , чтобы включить то , что Django делает по умолчанию через ModelBackend: if user.check_password(password) and self.user_can_authenticate(user):для того , чтобы убедиться , что пользователь имеет is_active=True.
jmq

1
Разве это не уязвимо для временной атаки, поскольку оно не включает в себя защиту от Django в исходном коде?
Габриэль Гарсия

Теперь тело запроса должно содержать такие поля, как имя пользователя и пароль. Есть ли способ изменить его на адрес электронной почты и пароль?
Karol

55

Если вы начинаете новый проект, django настоятельно рекомендует вам настроить пользовательскую модель. (см. https://docs.djangoproject.com/en/dev/topics/auth/customizing/#using-a-custom-user-model-when-starting-a-project )

и если вы это сделали, добавьте в свою модель пользователя три строки:

class MyUser(AbstractUser):
    USERNAME_FIELD = 'email'
    email = models.EmailField(_('email address'), unique=True) # changes email to unique and blank to false
    REQUIRED_FIELDS = [] # removes email from REQUIRED_FIELDS

Потом authenticate(email=email, password=password)работает, пока authenticate(username=username, password=password)перестает работать.


3
При запуске createuperuser это само выдает ошибку: TypeError: create_superuser () отсутствует 1 обязательный позиционный аргумент: 'username'. Вам необходимо использовать настраиваемый пользовательский менеджер: class MyUserManager(BaseUserManager): def create_superuser(self, email, password, **kwargs): user = self.model(email=email, is_staff=True, is_superuser=True, **kwargs) user.set_password(password) user.save() return user
Михал Голуб

18
Полные инструкции здесь: fomfus.com/articles/…
user2061057

1
С другой стороны, документы Django не рекомендуют использовать настраиваемую модель пользователя, если вы создаете многоразовое приложение.
djvg

14

Аутентификация электронной почты для Django 3.x

Для использования электронной почты / имени пользователя и пароля для аутентификации вместо аутентификации по умолчанию и пароля нам необходимо переопределить два метода класса ModelBackend: authentication () и get_user ():

Метод get_user принимает user_id - который может быть именем пользователя, идентификатором базы данных или чем-то еще, но должен быть уникальным для вашего пользовательского объекта - и возвращает пользовательский объект или None. Если вы не сохранили электронную почту в качестве уникального ключа, вам придется позаботиться о нескольких результатах, возвращаемых для query_set. В приведенном ниже коде об этом позаботились, вернув первого пользователя из возвращенного списка.

from django.contrib.auth.backends import ModelBackend, UserModel
from django.db.models import Q

class EmailBackend(ModelBackend):
    def authenticate(self, request, username=None, password=None, **kwargs):
        try: #to allow authentication through phone number or any other field, modify the below statement
            user = UserModel.objects.get(Q(username__iexact=username) | Q(email__iexact=username))
        except UserModel.DoesNotExist:
            UserModel().set_password(password)
        except MultipleObjectsReturned:
            return User.objects.filter(email=username).order_by('id').first()
        else:
            if user.check_password(password) and self.user_can_authenticate(user):
                return user

    def get_user(self, user_id):
        try:
            user = UserModel.objects.get(pk=user_id)
        except UserModel.DoesNotExist:
            return None

        return user if self.user_can_authenticate(user) else None

По умолчанию для AUTHENTICATION_BACKENDS установлено значение:

['django.contrib.auth.backends.ModelBackend']

В файле settings.py добавьте следующее внизу, чтобы переопределить значение по умолчанию:

AUTHENTICATION_BACKENDS = ('appname.filename.EmailBackend',)

Это здорово, спасибо. Я практически выполнил проект, шаблоны, формы, представления и многое другое, так что начинать заново - не так уж и привлекательно! Теперь я могу аутентифицироваться по адресу электронной почты, есть ли способ удалить поле имени пользователя, чтобы оно не было включено в аутентификацию и формы, отображаемые в шаблонах?
moonraker

11

У меня было аналогичное требование, когда любое имя пользователя / адрес электронной почты должны работать для поля имени пользователя. Если кто-то ищет способ аутентификации на бэкэнд для этого, ознакомьтесь со следующим рабочим кодом. Вы можете изменить набор запросов, если вам нужен только адрес электронной почты.

from django.contrib.auth import get_user_model  # gets the user_model django  default or your own custom
from django.contrib.auth.backends import ModelBackend
from django.db.models import Q


# Class to permit the athentication using email or username
class CustomBackend(ModelBackend):  # requires to define two functions authenticate and get_user

    def authenticate(self, username=None, password=None, **kwargs):
        UserModel = get_user_model()

        try:
            # below line gives query set,you can change the queryset as per your requirement
            user = UserModel.objects.filter(
                Q(username__iexact=username) |
                Q(email__iexact=username)
            ).distinct()

        except UserModel.DoesNotExist:
            return None

        if user.exists():
            ''' get the user object from the underlying query set,
            there will only be one object since username and email
            should be unique fields in your models.'''
            user_obj = user.first()
            if user_obj.check_password(password):
                return user_obj
            return None
        else:
            return None

    def get_user(self, user_id):
        UserModel = get_user_model()
        try:
            return UserModel.objects.get(pk=user_id)
        except UserModel.DoesNotExist:
            return None

Также добавьте AUTHENTICATION_BACKENDS = ('path.to.CustomBackend',) в settings.py


У меня это работало, пока я не обновился с 1.11 до 2.1.5. Есть идеи, почему это не работает с этой версией?
zerohedge 02

@zerohedge добавляет запрос к параметрам метода аутентификации. См. Docs.djangoproject.com/en/2.2/topics/auth/customizing/…
Ван,

Это также оставляет вас уязвимым для атаки по времени, стоит попытаться тщательно имитировать реализацию Django, чтобы предотвратить такие уязвимости: github.com/django/django/blob/master/django/contrib/auth/…
Габриэль Гарсия,

Это также позволит неактивным пользователям проходить аутентификацию.
Габриэль Гарсия

5

Django 2.x

Как упоминал Ганеш выше для django 2.x, для метода аутентификации теперь требуется параметр запроса.

# backends.py
from django.contrib.auth import backends, get_user_model
from django.db.models import Q
UserModel = get_user_model()


class ModelBackend(backends.ModelBackend):

    def authenticate(self, request, username=None, password=None, **kwargs):
        if username is None:
            username = kwargs.get(UserModel.USERNAME_FIELD)
        try:
            # user = UserModel._default_manager.get_by_natural_key(username)
            # You can customise what the given username is checked against, here I compare to both username and email fields of the User model
            user = UserModel.objects.get(Q(username__iexact=username) | Q(email__iexact=username))
        except UserModel.DoesNotExist:
            # Run the default password hasher once to reduce the timing
            # difference between an existing and a nonexistent user (#20760).
            UserModel().set_password(password)
        else:
            if user.check_password(password) and self.user_can_authenticate(user):
                return user
        return super().authenticate(request, username, password, **kwargs)

добавьте свой бэкэнд в настройки вашего проекта

# settings.py
AUTHENTICATION_BACKENDS = ['path.to.ModelBackend']

Ваша пользовательская модель пользователя должна будет сделать электронные письма уникальными для активных и проверенных пользователей, вы можете сделать это просто с помощью чего-то вроде этого:

from django.contrib.auth.models import AbstractUser


class User(AbstractUser):
    objects = UserManager()
    email = models.EmailField(_('email address'), unique=True)

    class Meta:
        verbose_name = _('user')
        verbose_name_plural = _('users')
        db_table = 'auth_user'
        swappable = 'AUTH_USER_MODEL'

Но для предотвращения того, чтобы кто-то блокировал использование своей электронной почты кем-то другим, вы должны вместо этого добавить проверку электронной почты и принять во внимание процесс регистрации и входа в систему, что электронные письма могут не быть уникальными (и, вероятно, помешать новым пользователям использовать существующий и проверенный адрес электронной почты).


4

Электронная почта и аутентификация имени пользователя для Django 2.X

Имея в виду, что это частый вопрос, вот пользовательская реализация, имитирующая исходный код Django, но которая аутентифицирует пользователя либо по имени пользователя, либо по электронной почте без учета регистра, сохраняя защиту от временных атак и не аутентифицируя неактивных пользователей .

from django.contrib.auth.backends import ModelBackend, UserModel
from django.db.models import Q

class CustomBackend(ModelBackend):
    def authenticate(self, request, username=None, password=None, **kwargs):
        try:
            user = UserModel.objects.get(Q(username__iexact=username) | Q(email__iexact=username))
        except UserModel.DoesNotExist:
            UserModel().set_password(password)
        else:
            if user.check_password(password) and self.user_can_authenticate(user):
                return user

    def get_user(self, user_id):
        try:
            user = UserModel.objects.get(pk=user_id)
        except UserModel.DoesNotExist:
            return None

        return user if self.user_can_authenticate(user) else None

Всегда не забывайте добавлять в свой settings.py правильный бэкэнд аутентификации .


1
Правильно ли я понимаю, что UserModel().set_password(password)существует возможность помешать злоумышленникам определить, существует ли пользователь или нет, независимо от выполнения примерно того же объема криптографической работы (я предполагаю, что это временная атака, которую вы имели в виду)?
Гранд-Пхуба

2

Вам следует настроить класс ModelBackend. Мой простой код:

from django.contrib.auth.backends import ModelBackend
from django.contrib.auth import get_user_model

class YourBackend(ModelBackend):

  def authenticate(self, username=None, password=None, **kwargs):
    UserModel = get_user_model()
    if username is None:
        username = kwargs.get(UserModel.USERNAME_FIELD)
    try:
        if '@' in username:
            UserModel.USERNAME_FIELD = 'email'
        else:
            UserModel.USERNAME_FIELD = 'username'

        user = UserModel._default_manager.get_by_natural_key(username)
    except UserModel.DoesNotExist:
        UserModel().set_password(password)
    else:
        if user.check_password(password) and self.user_can_authenticate(user):
            return user

И в файле settings.py добавьте:

AUTHENTICATION_BACKENDS = ['path.to.class.YourBackend']

Обновите свой код, чтобы включить requestпараметр в authenticateметод для django 2.1.1
Ганеш

2

Аутентификация по электронной почте и имени пользователя для Django 2.x

from django.contrib.auth import get_user_model
from django.contrib.auth.backends import ModelBackend
from django.db.models import Q

class EmailorUsernameModelBackend(ModelBackend):
    def authenticate(self, request, username=None, password=None, **kwargs):
        UserModel = get_user_model()
        try:
            user = UserModel.objects.get(Q(username__iexact=username) | Q(email__iexact=username))
        except UserModel.DoesNotExist:
            return None
        else:
            if user.check_password(password):
                return user
        return None

В settings.py добавьте следующую строку,

AUTHENTICATION_BACKENDS = ['appname.filename.EmailorUsernameModelBackend']

1
from django.contrib.auth.models import User

from django.db import Q

class EmailAuthenticate(object):

    def authenticate(self, username=None, password=None, **kwargs):
        try:
            user = User.objects.get(Q(email=username) | Q(username=username))
        except User.DoesNotExist:
            return None
        except MultipleObjectsReturned:
            return User.objects.filter(email=username).order_by('id').first()

        if user.check_password(password):
            return user
        return None

    def get_user(self,user_id):
        try:
            return User.objects.get(pk=user_id)
        except User.DoesNotExist:
            return None

А потом в settings.py:

AUTHENTICATION_BACKENDS = (
  'articles.backends.EmailAuthenticate',
)

где статьи - это мое приложение django, backends.pyэто файл python внутри моего приложения и EmailAuthenticateкласс бэкэнда аутентификации внутри моего backends.pyфайла


1

Довольно просто. В дополнительных занятиях нет необходимости.

Когда вы создаете и обновляете пользователя с помощью электронной почты, просто укажите в поле имени пользователя адрес электронной почты.

Таким образом, при аутентификации поле имени пользователя будет иметь то же значение, что и электронное письмо.

Код:

# Create
User.objects.create_user(username=post_data['email'] etc...)

# Update
user.username = post_data['email']
user.save()

# When you authenticate
user = authenticate(username=post_data['email'], password=password)

1
Добавьте образец кода, чтобы продемонстрировать, как ваш ответ может помочь в решении проблемы.
Suit Boy Apps

1
@CasmanRidder Ваш ответ будет удален, если вы не добавите дополнительную информацию.
Представитель 10 говорит, что сделайте прививку


0

Аутентификация по электронной почте для Django 2.x

def admin_login(request):
if request.method == "POST":
    email = request.POST.get('email', None)
    password = request.POST.get('password', None)
    try:
        get_user_name = CustomUser.objects.get(email=email)
        user_logged_in =authenticate(username=get_user_name,password=password)
        if user_logged_in is not None:
            login(request, user_logged_in)
            messages.success(request, f"WelcomeBack{user_logged_in.username}")
            return HttpResponseRedirect(reverse('backend'))
        else:
            messages.error(request, 'Invalid Credentials')
            return HttpResponseRedirect(reverse('admin_login'))
    except:
        messages.warning(request, 'Wrong Email')
        return HttpResponseRedirect(reverse('admin_login'))

else:
    if request.user.is_authenticated:
        return HttpResponseRedirect(reverse('backend'))
    return render(request, 'login_panel/login.html')

Можете ли вы добавить немного текста, чтобы объяснить, что делает ваш ответ и как он помогает ответить на вопрос?
Jaquez

Отредактировано. Спасибо
Шакил Ахмед

0

Если вы создали настраиваемую базу данных, оттуда, если вы хотите проверить свой электронный идентификатор и пароль.

  1. Получите идентификатор электронной почты и пароль с помощью models.objects.value_list('db_columnname').filter(db_emailname=textbox email)

2. назначить в полученном списке object_query_list

3. преобразовать список в строку

Пример:

  1. Возьмите HTML Email_idи Passwordзначения вViews.py

    u_email = request.POST.get('uemail')

    u_pass = request.POST.get('upass')

  2. Получить идентификатор электронной почты и пароль из базы данных

    Email = B_Reg.objects.values_list('B_Email',flat=True).filter(B_Email=u_email)

    Password = B_Reg.objects.values_list('Password',flat=True).filter(B_Email=u_email)

  3. Возьмите значения идентификатора электронной почты и пароля в списке из Queryнабора значений

    Email_Value = Email[0]

    Password_Value=Password[0]

  4. Преобразовать список в строку

    string_email = ''.join(map(str, Email_Value))

    string_password = ''.join(map(str, Password_Value))

Наконец, ваше условие входа в систему

if (string_email==u_email and string_password ==u_pass)

0

Я создал для этого помощника: функцию authenticate_user(email, password).

from django.contrib.auth.models import User


def authenticate_user(email, password):
    try:
        user = User.objects.get(email=email)
    except User.DoesNotExist:
        return None
    else:
        if user.check_password(password):
            return user

    return None

class LoginView(View):
    template_name = 'myapp/login.html'

    def get(self, request):
        return render(request, self.template_name)

    def post(self, request):
        email = request.POST['email']
        password = request.POST['password']
        user = authenticate_user(email, password)
        context = {}

        if user is not None:
            if user.is_active:
                login(request, user)

                return redirect(self.request.GET.get('next', '/'))
            else:
                context['error_message'] = "user is not active"
        else:
            context['error_message'] = "email or password not correct"

        return render(request, self.template_name, context)

0

Похоже, что этот метод был обновлен в Django 3.0.

Для меня метод работы был:

authentication.py # <- я поместил это в приложение (не работает в папке проекта вместе с settings.py

from django.contrib.auth import get_user_model
from django.contrib.auth.backends import BaseBackend
from django.contrib.auth.hashers import check_password
from django.contrib.auth.models import User

class EmailBackend(BaseBackend):
    def authenticate(self, request, username=None, password=None, **kwargs):
        UserModel = get_user_model()
        try:
            user = UserModel.objects.get(email=username)
        except UserModel.DoesNotExist:
            return None
        else:
            if user.check_password(password):
                return user
        return None

    def get_user(self, user_id):
        UserModel = get_user_model()
        try:
            return UserModel.objects.get(pk=user_id)
        except UserModel.DoesNotExist:
            return None

Затем добавил это в файл settings.py

AUTHENTICATION_BACKENDS = (
    'appname.authentication.EmailBackend',
)

0

Модель пользователя по умолчанию наследует / расширяет абстрактный класс. Структура должна быть терпимой к определенному количеству изменений или дополнений.

Более простой способ взлома - это сделать следующее: Это в виртуальной среде.

  1. Перейдите в место установки django и найдите Lib папку
  2. перейдите в django / contrib / auth /
  3. найдите и откройте файл models.py. Найдите строку 315 класса AbstractUser

СТРОКА 336 в атрибуте электронной почты добавьте уникальный и установите для него значение true

email = models.EmailField(_('email address'), blank=True,unique=True)

USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['username']
  1. Готово, выполните миграцию и перенесите

Делайте это на свой страх и риск,

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.