Django - 用电子邮件登录

问题描述 投票:48回答:7

我希望django通过电子邮件验证用户,而不是通过用户名。一种方法是提供电子邮件值作为用户名值,但我不希望这样。原因是,我有一个网址/profile/<username>/,因此我不能有一个网址/profile/[email protected]/

另一个原因是所有电子邮件都是唯一的,但有时会发生用户名。因此我将自动创建用户名为fullName_ID

我怎样才能让Django通过电子邮件进行身份验证?

这就是我创建用户的方式。

username = `abcd28`
user_email = `[email protected]`
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)

除了首先获取用户名之外,还有其他任何登录信息吗?

django
7个回答
72
投票

您应该编写自定义身份验证后端。这样的东西会起作用:

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

class EmailBackend(ModelBackend):
    def authenticate(self, 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()这样的方法。


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')

27
投票

如果您正在开始一个新项目,django强烈建议您设置自定义用户模型。 (见https://docs.djangoproject.com/en/1.10/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)停止工作。


9
投票

我有一个类似的要求,其中用户名/电子邮件应该用于用户名字段。如果有人正在寻找身份验证后端的方法,请查看以下工作代码。如果您只需要电子邮件,您可以更改查询集。

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

还要在settings.py中添加AUTHENTICATION_BACKENDS =('path.to.CustomBackend',)


3
投票

Django 2.x

正如Ganesh上面提到的django 2.x,authenticate方法现在需要一个请求参数。

# 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'

但是为了防止有人阻止其他人使用他们的电子邮件,您应该添加电子邮件验证,并让您的注册和登录过程考虑到电子邮件可能不是唯一的(并且可能阻止新用户使用现有和经过验证的电子邮件地址)。


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']

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-app,backends.py是我的app中的python文件,EmailAuthenticate是我的backends.py文件中的认证后端类


1
投票

Django 2.X的电子邮件和用户名验证

记住这是一个常见的问题,这是一个模仿Django source code的自定义实现,但用用户名或电子邮件验证用户,不区分大小写,保持timing attack protectionnot authenticating inactive users

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添加到正确的Authentication Backend


0
投票

对于Django 2

username = get_object_or_404(User, email=data["email"]).username
        user = authenticate(
            request, 
            username = username, 
            password = data["password"]
        )
        login(request, user)

0
投票

使用电子邮件和用户名进行身份验证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']
© www.soinside.com 2019 - 2024. All rights reserved.