浮光商城10-扩展JWT

01. 自定义载荷

JWT的payload中,默认返回值的token只有username和user_id以及email,

实际开发中,我们往往还需在客户端页面中显示当前登陆用户的其他信息(例如:头像),

也就是说,我们需要把额外的用户信息添加到jwt的返回结果中。

这时,我们需要重写rest_framework_jwt中生成jwt的函数。

下面分析源代码:

image-20230606130357818

image-20230606130407121

image-20230606130412476

image-20230606130416024

image-20230606130419865

image-20230606130913915

在这里插入图片描述

image-20230606131149583

image-20230606131307844

image-20230606131448212

通过修改该函数的返回值可以完成我们的需求。

在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 的配置信息。

所以,我们只需要将在项目的配置信息中,重写以下这一句即可:

image-20230606131847689

修改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. 多条件登录

默认只能用用户名与密码进行登录。

如果我们需要再加上手机号与密码、邮箱与密码登录,就需要对原有的功能进行扩展。

首先,我们需要了解,用户登录时,后端是在哪里进行数据验证的。

从源代码分析开始:

image-20230606130357818

image-20230606130407121

image-20230606130412476

image-20230606130416024

image-20230606130419865

image-20230606130423641

image-20230606130427708

image-20230606130431508

image-20230606132510170

image-20230606132812890

image-20230606130439698

image-20230606130443658

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方法的思路:

  1. 根据username参数查找用户User对象,username参数可能是用户名,也可能是手机号
  2. 若查找到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', ]

这样,就支持以用户名、手机号、邮箱等登录账户了。

评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值