JWT扩展的登录视图,在收到用户名与密码时,也是调用Django的认证系统中提供的authenticate()来检查用户名与密码是否正确。
我们可以通过修改Django认证系统的认证后端(主要是authenticate方法)来支持登录账号既可以是用户名也可以是手机号。
修改Django认证系统的认证后端需要继承django.contrib.auth.backends.ModelBackend
,并重写authenticate方法。
authenticate(self, request, username=None, password=None, **kwargs)
方法的参数说明:
- request 本次认证的请求对象
- username 本次认证提供的用户账号
- password 本次认证提供的密码
我们想要让用户既可以以用户名登录,也可以以手机号登录,那么对于authenticate方法而言,username参数即表示用户名或者手机号。
重写authenticate方法的思路:
- 根据username参数查找用户User对象,username参数可能是用户名,也可能是手机号
- 若查找到User对象,调用User对象的check_password方法检查密码是否正确
在users/utils.py中编写:
def get_user_by_account(account):
"""
根据帐号获取user对象
:param account: 账号,可以是用户名,也可以是手机号
:return: User对象 或者 None
"""
try:
if re.match('^1[3-9]\d{9}$', account):
# 帐号为手机号
user = User.objects.get(mobile=account)
else:
# 帐号为用户名
user = User.objects.get(username=account)
except User.DoesNotExist:
return None
else:
return user
class UsernameMobileAuthBackend(ModelBackend):
"""
自定义用户名或手机号认证
"""
def authenticate(self, request, username=None, password=None, **kwargs):
user = get_user_by_account(username)
if user is not None and user.check_password(password):
return user
在配置文件中告知Django使用我们自定义的认证后端
AUTHENTICATION_BACKENDS = [
'users.utils.UsernameMobileAuthBackend',
]
登录
1. 业务说明
验证用户名和密码,验证成功后,为用户签发JWT,前端将签发的JWT保存下来。
2. 后端接口设计
请求方式: POST /authorizations/
请求参数: JSON 或 表单
参数名 | 类型 | 是否必须 | 说明 |
---|---|---|---|
username | str | 是 | 用户名 |
password | str | 是 | 密码 |
返回数据: JSON
{
"username": "python",
"user_id": 1,
"token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjo5LCJ1c2VybmFtZSI6InB5dGhvbjgiLCJleHAiOjE1MjgxODI2MzQsImVtYWlsIjoiIn0.ejjVvEWxrBvbp18QIjQbL1TFE0c0ejQgizui_AROlAU"
}
返回值 | 类型 | 是否必须 | 说明 |
---|---|---|---|
username | str | 是 | 用户名 |
user_id | int | 是 | 用户id |
token | str | 是 | 身份认证凭据 |
3. 后端实现
Django REST framework JWT提供了登录签发JWT的视图,可以直接使用
from rest_framework_jwt.views import obtain_jwt_token
urlpatterns = [
url(r'^authorizations/$', obtain_jwt_token),
]
但是默认的返回值仅有token,我们还需在返回值中增加username和user_id。
通过修改该视图的返回值可以完成我们的需求。
在users/utils.py 中,创建
def jwt_response_payload_handler(token, user=None, request=None):
"""
自定义jwt认证成功返回数据
"""
return {
'token': token,
'user_id': user.id,
'username': user.username
}
修改配置文件
# JWT
JWT_AUTH = {
'JWT_EXPIRATION_DELTA': datetime.timedelta(days=1), #jwt过期时间设置
'JWT_RESPONSE_PAYLOAD_HANDLER': 'users.utils.jwt_response_payload_handler',
}