01. 自定义载荷
JWT的payload中,默认返回值的token只有username和user_id以及email,
实际开发中,我们往往还需在客户端页面中显示当前登陆用户的其他信息(例如:头像),
也就是说,我们需要把额外的用户信息添加到jwt的返回结果中。
这时,我们需要重写rest_framework_jwt中生成jwt的函数。
下面分析源代码:










通过修改该函数的返回值可以完成我们的需求。
在utils/authenticate.py 中,创建jwt_payload_handler函数重写返回值。
其主体功能仍然是
rest_framework_jwt.utils.jwt_decode_handler实现的,我们只需要在其返回前,额外添加我们所需要的字段即可。
from rest_framework_jwt.utils import jwt_payload_handler as payload_handler
def jwt_payload_handler(user):
"""
自定义载荷信息
:params user 用户模型实例对象
"""
# 先让jwt模块生成自己的载荷信息
payload = payload_handler(user)
# 追加自己要返回的内容
if hasattr(user, 'avatar'):
payload['avatar'] = user.avatar.url if user.avatar else ""
if hasattr(user, 'nickname'):
payload['nickname'] = user.nickname
if hasattr(user, 'money'):
payload['money'] = float(user.money)
if hasattr(user, 'credit'):
payload['credit'] = user.credit
return payload
扩展了原来的jwt token生成函数之后,我们需要将新写的函数写入配置文件中。
项目的配置文件信息,优先级高于
rest_framework_jwt.settings的配置信息。所以,我们只需要将在项目的配置信息中,重写以下这一句即可:
修改settings/dev.py配置文件
import datetime
# jwt认证相关配置项
JWT_AUTH = {
# 设置jwt的有效期
# 如果内部站点,例如:运维开发系统,OA,往往配置的access_token有效期基本就是15分钟,30分钟,1~2个小时
'JWT_EXPIRATION_DELTA': datetime.timedelta(weeks=1), # 一周有效,
# 自定义载荷
'JWT_PAYLOAD_HANDLER': 'fuguang_api.utils.authenticate.jwt_payload_handler',
}
02. 多条件登录
默认只能用用户名与密码进行登录。
如果我们需要再加上手机号与密码、邮箱与密码登录,就需要对原有的功能进行扩展。
首先,我们需要了解,用户登录时,后端是在哪里进行数据验证的。
从源代码分析开始:












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方法检查密码是否正确
在utils/authenticate.py中编写:
from rest_framework_jwt.utils import jwt_payload_handler as payload_handler
from django.contrib.auth.backends import ModelBackend, UserModel
from django.contrib.auth import get_user_model
from django.db.models import Q
def jwt_payload_handler(user):
"""
自定义载荷信息
:params user 用户模型实例对象
"""
# 先让jwt模块生成自己的载荷信息
payload = payload_handler(user)
# 追加自己要返回的字段内容
if hasattr(user, 'avatar'):
payload['avatar'] = user.avatar.url if user.avatar else ""
if hasattr(user, 'nickname'):
payload['nickname'] = user.nickname
if hasattr(user, 'money'):
payload['money'] = float(user.money)
if hasattr(user, 'credit'):
payload['credit'] = user.credit
return payload
def get_user_by_account(account):
"""
根据帐号信息获取user模型实例对象
:param account: 账号信息,可以是用户名,也可以是手机号,甚至其他的可用于识别用户身份的字段信息
:return: User对象 或者 None
"""
user = UserModel.objects.filter(Q(mobile=account) | Q(username=account) | Q(email=account)).first()
return user
class CustomAuthBackend(ModelBackend):
"""
自定义用户认证类[实现多条件登录]
"""
def authenticate(self, request, username=None, password=None, **kwargs):
"""
多条件认证方法
:param request: 本次客户端的http请求对象
:param username: 本次客户端提交的用户信息,可以是user,也可以mobile或其他唯一字段
:param password: 本次客户端提交的用户密码
:param kwargs: 额外参数
:return:
"""
if username is None:
username = kwargs.get(UserModel.USERNAME_FIELD)
if username is None or password is None:
return
try:
# 注释掉django中的源代码
# user = UserModel._default_manager.get_by_natural_key(username)
user = get_user_by_account(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
在配置文件settings/dev.py中告知Django使用我们自定义的认证后端,注意不是给drf添加设置。
# django自定义认证
AUTHENTICATION_BACKENDS = ['luffycityapi.utils.authenticate.CustomAuthBackend', ]
这样,就支持以用户名、手机号、邮箱等登录账户了。

9992





