Django rest 框架 JWT 和自定义身份验证后端
Posted
技术标签:
【中文标题】Django rest 框架 JWT 和自定义身份验证后端【英文标题】:Django rest framework JWT and custom authentication backend 【发布时间】:2017-10-27 19:53:51 【问题描述】:我有一个自定义用户模型并创建了一个自定义身份验证后端。我正在使用django rest framework 和django rest framework JWT 进行令牌认证。
用户模型:
class User(AbstractBaseUser, PermissionsMixin):
email = models.EmailField(
unique=True,
max_length=254,
)
first_name = models.CharField(max_length=15)
last_name = models.CharField(max_length=15)
mobile = models.IntegerField(unique=True)
date_joined = models.DateTimeField(default=timezone.now)
is_active = models.BooleanField(default=True)
is_admin = models.BooleanField(default=False)
objects = UserManager()
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['first_name', 'last_name', 'mobile']
身份验证后端:
class EmailOrMobileAuthBackend(object):
def authenticate(self, username=None, password=None):
try:
user = get_user_model().objects.get(email=username)
if user.check_password(password):
return user
except User.DoesNotExist:
if username.isdigit():
try:
user = get_user_model().objects.get(mobile=username)
if user.check_password(password):
return user
except User.DoesNotExist:
return None
else:
return None
def get_user(self, user_id):
try:
return get_user_model().objects.get(pk=user_id)
except User.DoesNotExist:
return None
并且已经在settings.py中添加了:
AUTHENTICATION_BACKENDS = ('accounts.email_mobile_auth_backend.EmailOrMobileAuthBackend',)
登录 django 管理站点时,电子邮件和手机号码都可以很好地验证用户身份。但是,当我尝试使用django rest framework JWT 为用户获取令牌时,出现错误:
curl -X POST -d "email=admin@gmail.com&password=123123" http://localhost/api-token-auth/
"non_field_errors": [
"Unable to log in with provided credentials."
]
我还在 Rest 框架的默认身份验证中添加了自定义身份验证后端类,但它仍然无法正常工作:
REST_FRAMEWORK =
...
'DEFAULT_AUTHENTICATION_CLASSES': (
'accounts.email_mobile_auth_backend.EmailOrMobileAuthBackend',
'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
),
我错过了什么?为什么在登录django管理站点时它可以工作,但是在使用django rest framework jwt获取令牌时出错?
更新
我按照建议创建了另一个身份验证后端并将其添加到DEFAULT_AUTHENTICATION_CLASSES
,但即使这样也不起作用。
class DrfAuthBackend(BaseAuthentication):
def authenticate(self, username=None, password=None):
try:
user = get_user_model().objects.get(email=username)
if user.check_password(password):
return user, None
except User.DoesNotExist:
if username.isdigit():
try:
user = get_user_model().objects.get(mobile=username)
if user.check_password(password):
return user, None
except User.DoesNotExist:
return None
else:
return None
设置:
REST_FRAMEWORK =
'DEFAULT_PERMISSION_CLASSES': (
'rest_framework.permissions.IsAdminUser',
),
'DEFAULT_AUTHENTICATION_CLASSES': (
'accounts.email_mobile_auth_backend.EmailOrMobileAuthBackend',
'accounts.email_mobile_auth_backend.DrfAuthBackend',
'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
),
更新
将身份验证类中的args
从username
更改为email
似乎可以获取auth_token
,但同样无法登录到管理站点。
class EmailOrMobileAuthBackend(object):
def authenticate(self, email=None, password=None):
try:
user = get_user_model().objects.get(email=email)
if user.check_password(password):
return user
except User.DoesNotExist:
if email.isdigit():
try:
user = get_user_model().objects.get(mobile=email)
if user.check_password(password):
return user
except User.DoesNotExist:
return None
else:
return None
def get_user(self, user_id):
try:
return get_user_model().objects.get(pk=user_id)
except User.DoesNotExist:
return None
【问题讨论】:
您最终解决了这个问题吗?在使用django-rest-framework
和django-rest-framework-jwt
时遇到了类似的问题,我需要允许用户通过用户名或电子邮件或mobile_number 登录来获取JWT 令牌
@lukik 我正在使用两个身份验证后端。一个用于 django 管理员,另一个用于其余 api。
这两个后端是否允许用户通过电子邮件或手机登录?如果是的话,你介意分享一下你是怎么做的,尤其是对于其余的 API 吗?谢谢。
@lukik 是的,当然。我创建了两个身份验证后端(两者基本相同)。你可以找到他们here 和there。然后在设置中我沿线添加了these。
【参考方案1】:
您应该检查 DRF documentation 以获取自定义身份验证后端。
我认为您的自定义身份验证后端破坏了它,您可以通过从 DRF 设置中删除您的来解决它:
REST_FRAMEWORK =
...
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
),
或者通过修复你的,这与 Django 自定义身份验证基本不同,因为你应该从 authentication.BaseAuthentication
扩展并返回一个元组。
from rest_framework import authentication
class DrfAuthBackend(authentication.BaseAuthentication):
def authenticate(self, email=None, password=None):
try:
user = get_user_model().objects.get(email=email)
if user.check_password(password):
return user, None
except User.DoesNotExist:
if email.isdigit():
try:
user = get_user_model().objects.get(mobile=email)
if user.check_password(password):
return user, None
except User.DoesNotExist:
return None
else:
return None
然后在 DRF 设置中使用它:
REST_FRAMEWORK =
'DEFAULT_PERMISSION_CLASSES': (
'rest_framework.permissions.IsAdminUser',
),
'DEFAULT_AUTHENTICATION_CLASSES': (
'accounts.email_mobile_auth_backend.DrfAuthBackend',
'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
),
对于 Django 登录:
class EmailOrMobileAuthBackend(object):
def authenticate(self, username=None, password=None):
try:
user = get_user_model().objects.get(email=username)
if user.check_password(password):
return user
except User.DoesNotExist:
if username.isdigit():
try:
user = get_user_model().objects.get(mobile=username)
if user.check_password(password):
return user
except User.DoesNotExist:
return None
else:
return None
def get_user(self, user_id):
try:
return get_user_model().objects.get(pk=user_id)
except User.DoesNotExist:
return None
然后进行设置:
AUTHENTICATION_BACKENDS = ('accounts.email_mobile_auth_backend.EmailOrMobileAuthBackend',)
【讨论】:
我已经尝试了您的建议并更新了问题,但仍然无法正常工作。但是,将authentication()
方法中的参数从username
更改为email
无需将其添加到DEFAULT_AUTHENTICATION_CLASSES
即可完成工作。但同样无法登录到管理站点。
The Django custom authentication 不支持电子邮件作为关键字参数,您应该只将它用于 Django 并为 DRF 写一个不同的【参考方案2】:
在您的 settings.py 文件中,添加以下内容:
AUTH_USER_MODEL = 'accounts.email_mobile_auth_backend.EmailOrMobileAuthBackend'
【讨论】:
以上是关于Django rest 框架 JWT 和自定义身份验证后端的主要内容,如果未能解决你的问题,请参考以下文章
使用 Django Rest 框架进行 JWT 令牌身份验证
如何在 django rest 框架中验证 jwt 身份验证中的令牌
Django Rest Framework 中的 JWT 身份验证错误“无效签名”