在django-rest-framework-simplejwt插件中,默认使用username
和password
。但是我想使用email
而不是username
。所以,我做了如下:
在序列化程序中:
class MyTokenObtainSerializer(Serializer):
username_field = User.EMAIL_FIELD
def __init__(self, *args, **kwargs):
super(MyTokenObtainSerializer, self).__init__(*args, **kwargs)
self.fields[self.username_field] = CharField()
self.fields['password'] = PasswordField()
def validate(self, attrs):
# self.user = authenticate(**{
# self.username_field: attrs[self.username_field],
# 'password': attrs['password'],
# })
self.user = User.objects.filter(email=attrs[self.username_field]).first()
print(self.user)
if not self.user:
raise ValidationError('The user is not valid.')
if self.user:
if not self.user.check_password(attrs['password']):
raise ValidationError('Incorrect credentials.')
print(self.user)
# Prior to Django 1.10, inactive users could be authenticated with the
# default `ModelBackend`. As of Django 1.10, the `ModelBackend`
# prevents inactive users from authenticating. App designers can still
# allow inactive users to authenticate by opting for the new
# `AllowAllUsersModelBackend`. However, we explicitly prevent inactive
# users from authenticating to enforce a reasonable policy and provide
# sensible backwards compatibility with older Django versions.
if self.user is None or not self.user.is_active:
raise ValidationError('No active account found with the given credentials')
return {}
@classmethod
def get_token(cls, user):
raise NotImplemented(
'Must implement `get_token` method for `MyTokenObtainSerializer` subclasses')
class MyTokenObtainPairSerializer(MyTokenObtainSerializer):
@classmethod
def get_token(cls, user):
return RefreshToken.for_user(user)
def validate(self, attrs):
data = super(MyTokenObtainPairSerializer, self).validate(attrs)
refresh = self.get_token(self.user)
data['refresh'] = text_type(refresh)
data['access'] = text_type(refresh.access_token)
return data
视野中:
class MyTokenObtainPairView(TokenObtainPairView):
"""
Takes a set of user credentials and returns an access and refresh JSON web
token pair to prove the authentication of those credentials.
"""
serializer_class = MyTokenObtainPairSerializer
而且它起作用了!
现在我的问题是,我怎样才能更有效地做这件事?谁能给我一些建议?谢谢。
6条答案
按热度按时间c90pui9n1#
为了简化身份验证后端,您需要连接到多个类。我建议您使用下面的option 1(以及可选的option 3,这是您的简化版本)。在阅读之前,请注意以下几点:
***注1:**django在创建用户时并不强制要求电子邮件是唯一的(你可以覆盖这一点,但这是题外话)!选项3(你的实现)可能会给予你带来重复电子邮件的问题。
***注意1b:**使用
User.objects.filter(email__iexact=...)
以不区分大小写的方式匹配电子邮件。***注1c:**使用
get_user_model()
以防将来替换默认用户模型,这对初学者来说真的是一个救命稻草!***注意2:**避免将用户打印到控制台。您可能正在打印敏感数据。
对于3个选项:
class EmailModelBackend(ModelBackend)
调整django认证后端并替换认证函数。authenticate(username=, password=, **kwarg)
authenticate(...)
调整其他应用程序,仅替换JWT auth(如果您这样设置)参数不是必需的,因此不建议使用此选项)。MyTokenObtainPairSerializer
。选项1(注意,这也允许用户名!!):
**选项2:**跳过,留给读者,不建议。
**选项3:**您似乎已在上文中涵盖了这一点。
注意:您不必定义
MyTokenObtainPairView
,您可以在urls.py中使用TokenObtainPairView(serializer_class=MyTokenObtainPairSerializer).as_view()
。覆盖所用标记序列化器的小简化。注2:您可以在www.example.com(或设置文件)中指定标识声明和添加的数据settings.py以使用电子邮件。这将使您的前端应用也使用声明的电子邮件(而不是默认的user.id)
但是,请注意创作者给出的独特性警告:
例如,指定“用户名”或“电子邮件”字段将是一个糟糕的选择,因为帐户的用户名或电子邮件可能会根据给定服务中帐户管理的设计方式而更改。
如果你能保证唯一性,你就万事俱备了。
6qftjkof2#
为什么你要复制粘贴这么多东西而不是子类化呢?我用它来工作:
还有
当然
q0qdq0h23#
这个问题已经有一段时间了,但是,我为@Mic的答案加了+1。顺便说一句,仅仅如下更新到
TokenObtainPairSerializer
还不够吗?von4xj4u4#
让我们总结一下上述解决方案:
1-使用Django命令创建两个应用程序,一个用于新令牌,另一个用于用户:
2-在m_token中,创建serializers.py并覆盖序列化程序以将username替换为email字段:
3-在m_token中,覆盖视图以使用新的序列化程序替换序列化程序:
4-在m_token中,创建urls.py并给予路径,如下所示:
5-在m_user中,覆盖用户模型,如下所示:
6-在django项目根目录中,将
AUTH_USER_MODEL = 'm_user.MUser'
添加到setting.py中。我在我的项目中测试了它,它工作得很完美。我希望我没有错过任何东西。这样,swagger也会在令牌参数中显示“email”而不是“username”:
l3zydbqr5#
除了@Mic的答案外,记住设置
USERNAME_FIELD = 'email'
,在用户模型中可能是REQUIRED_FIELDS = ['username']
。ssgvzors6#
对于那些使用自定义用户模型的用户,您只需添加以下行:
然后,在urls.py: