Django使用Djoser进行电子邮件认证登录

5

我最近尝试使用Djoser,并希望使用电子邮件而不是用户名进行登录。

Djoser:http://djoser.readthedocs.io/en/latest/index.html

然后,我尝试自定义令牌创建/登录以在serializers.py中将用户名更改为电子邮件。

原始:

class TokenCreateSerializer(serializers.Serializer):
password = serializers.CharField(
    required=False, style={'input_type': 'password'}
    )

    default_error_messages = {
        'invalid_credentials': constants.INVALID_CREDENTIALS_ERROR,
        'inactive_account': constants.INACTIVE_ACCOUNT_ERROR,
    }

    def __init__(self, *args, **kwargs):
        super(TokenCreateSerializer, self).__init__(*args, **kwargs)
    self.user = None
    self.fields[User.USERNAME_FIELD] = serializers.CharField(
        required=False
    )

def validate(self, attrs):
    self.user = authenticate(
        username=attrs.get(User.USERNAME_FIELD),
        password=attrs.get('password')
        )

        self._validate_user_exists(self.user)
        self._validate_user_is_active(self.user)
        return attrs

    def _validate_user_exists(self, user):
        if not user:
            self.fail('invalid_credentials')

    def _validate_user_is_active(self, user):
        if not user.is_active:
            self.fail('inactive_account')

编辑完成

class TokenCreateSerializer(serializers.Serializer):
password = serializers.CharField(
    required=False, style={'input_type': 'password'}
    )

    default_error_messages = {
        'invalid_credentials': constants.INVALID_CREDENTIALS_ERROR,
        'inactive_account': constants.INACTIVE_ACCOUNT_ERROR,
    }

    def __init__(self, *args, **kwargs):
        super(TokenCreateSerializer, self).__init__(*args, **kwargs)
    self.user = None
    self.fields[User.EMAIL_FIELD] = serializers.EmailField(
        required=False
    )

def validate(self, attrs):
    self.user = authenticate(
        email=attrs.get(User.EMAIL_FIELD),
        password=attrs.get('password')
        )

        self._validate_user_exists(self.user)
        self._validate_user_is_active(self.user)
        return attrs

    def _validate_user_exists(self, user):
        if not user:
            self.fail('invalid_credentials')

    def _validate_user_is_active(self, user):
        if not user.is_active:
            self.fail('inactive_account')

但是我在API中得到的结果是这样的。
{
"non_field_errors": [
    "Unable to login with provided credentials."
]

我尝试了其他方法,但结果都一样。有没有办法使用电子邮件进行身份验证,而不是用户名?

3个回答

13

Djoser使用django.contrib.auth中的authenticate方法。默认情况下,AUTHENTICATION_BACKENDS设置为使用django.contrib.auth.backends.ModelBackend,该方法将尝试使用默认管理器get_by_natural_key方法获取用户模型,其执行如下:

def get_by_natural_key(self, username):
    return self.get(**{self.model.USERNAME_FIELD: username})
您可以查看源代码来验证这一点。
我对此的做法是增强django的UserManager,假设您的User模型是AbstractUser的子类,尝试通过电子邮件或用户名获取用户,如下所示:
from django.contrib.auth.models import UserManager
from django.db.models import Q

class CustomUserManager(UserManager):

    def get_by_natural_key(self, username):
        return self.get(
            Q(**{self.model.USERNAME_FIELD: username}) |
            Q(**{self.model.EMAIL_FIELD: username})
        )

在您的User模型中使用它

from django.contrib.auth.models import AbstractUser

class User(AbstractUser):
    ...
    objects = CustomUserManager()

现在您应该能够使用电子邮件或用户名登录


12

我在寻找这个问题的答案时,找到了这个帖子!

一切都有效,但后来我发现了一个更简单的解决方法:

DJOSER = {
    'LOGIN_FIELD': 'email'
}

我不知道,也许有人会需要它!


2

我是这样意识到的

from django.contrib.auth import authenticate, get_user_model
from rest_framework import serializers

from djoser.compat import get_user_email_field_name
from djoser.conf import settings


User = get_user_model()


class TokenCreateSerializer(serializers.Serializer):
    password = serializers.CharField(required=False, style={"input_type": "password"})

    default_error_messages = {
        "invalid_credentials": settings.CONSTANTS.messages.INVALID_CREDENTIALS_ERROR,
        "inactive_account": settings.CONSTANTS.messages.INACTIVE_ACCOUNT_ERROR,
    }

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.user = None

        self.email_field = get_user_email_field_name(User)
        self.fields[self.email_field] = serializers.EmailField()

    def validate(self, attrs):
        password = attrs.get("password")
        email = attrs.get("email")
        self.user = authenticate(
            request=self.context.get("request"), email=email, password=password
        )
        if not self.user:
            self.user = User.objects.filter(email=email).first()
            if self.user and not self.user.check_password(password):
                self.fail("invalid_credentials")
        if self.user and self.user.is_active:
            return attrs
        self.fail("invalid_credentials")

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接