08-接口文档和JWT

接口文档

楔子

接口文档对于协调前后端开发非常重要,可以避免因为开发习惯不同而导致的意外情况。在项目中,如果前后端开发各自为战,可能会出现不一致的情况。因此,接口文档可以约束双方,确保他们按照统一的规范进行开发,从而提高协同开发的效率和一致性。

规范

接口文档一般包括以下内容:

  1. 接口描述: 对每个接口进行描述,包括其功能、输入参数、输出格式和预期行为。

  2. 请求方法: 每个接口支持的请求方法,例如 GET、POST、PUT、DELETE 等。

  3. 请求参数: 每个接口所需的请求参数,包括参数名、类型、是否必需等信息。

  4. 响应格式: 每个接口的响应格式,包括响应数据的结构、类型等信息。

  5. 错误处理: 定义了接口可能返回的错误状态码和对应的错误信息,以及客户端应该如何处理这些错误。

  6. 示例: 可能包括一些请求和响应的示例,以便开发人员更好地理解接口的使用方式。

  7. 安全要求: 如果接口需要进行身份验证或授权,接口文档应包括相应的安全要求和机制。

  8. 版本控制: 如果接口存在多个版本,接口文档应该明确标明每个版本的区别和兼容性信息。

接口文档展现形式

  1. word形式
  2. markdown形式
  3. json和yaml
  4. 公司自研
  5. 开源的接口文档平台
    1. 比如Yapi 百度开源的
  6. 通过项目,自动生成接口文档平台
    1. coreapi
    2. drf-yasg
  7. 接口文档命名,建议包含版本字样,比如:书籍请求接口-v1.md

一个简单的接口文档实例

# 1. 用户相关
## 1.1 登录接口
	# 请求地址
	# 请求参数
	# 编码格式
	# 响应字段详解
## 1.2 注册接口
	# 请求地址
	# 请求参数
	# 编码格式
	# 响应字段详解

	# ...
	

image-20240418185146775

自动生成接口文档 coreapi (了解)

安装

pip install coreapi

使用

# 路由层
from rest_framework.documentation import include_docs_urls

urlpatterns = [
    path("docs/", include_docs_urls(title="书籍查询接口文档"))
]

# 视图层
from rest_framework.viewsets import ModelViewSet
from .serializer import BookSerializer
from .models import Book

class BookViewSet(ModelViewSet):
    queryset = Book.objects.all()
    serializer_class = BookSerializer

如果遇到报错

#AttributeError: 'AutoSchema' object has no attribute 'get_link'
REST_FRAMEWORK = {
 'DEFAULT_SCHEMA_CLASS': 'rest_framework.schemas.coreapi.AutoSchema',
    # 新版drf schema_class默认用的是rest_framework.schemas.openapi.AutoSchema
}

启动项目:http://127.0.0.1:8000/app01/docs/

image-20240418200748921

说明

  1. 模型层种的 help_text 控制描述文本,即上图种的出版社
  2. 因为序列化层和模型层的字段基本一样,所以序列化层设置一样的,二选一即可。
  3. 必填和不必填,取决于序列化类中的required参数默认为True,不必填写成False即可。
  4. 方法下的3引号注释,会生成介绍,response的文本会返回结果
def list(self, request):
    """
        查询全部书籍的接口
        """
    return Response("接口描述")

image-20240418202428219

jwt

详细介绍:https://www.ruanyifeng.com/blog/2018/07/json_web_token-tutorial.html

JSON Web Token(缩写 JWT)是目前最流行的跨域认证解决方案。

  1. 使用jwt 必须使用django自带的user表。

  2. 并不限制语言,所有的web框架都可以采用这种认证方式。

  3. 三段式,用 . 去分割 每一段使用base64编码 (base64不是加密方案,只是编码方案)

    1. 头 一般放:公司信息,加密方式,是jwt,一般都是固定的
      1. header
    2. 荷载 一般放:登录用户的信息:用户id,过期时间,签发时间,是否是超级用户。 不能放用户密码!,能加能删,一般会往里面加东西,而不会删东西。
      1. payload
    3. 签名 signature 二进制数据 校验阶段主要是第三段。
      1. 签发阶段:通过头和荷载 使用 某种加密方式[HS256,md5,sha1等]加密得到。
      2. 校验阶段:拿到token,取出第一和第二部分,通过同样的加密方式获得新前面,用新的签名对比第三段(老签名)比较,如果一样,说明数据没有被修改,信任,正常处理。如果不一样,说明数据被篡改或者是模拟生成的,不能信任,返回错误。
        1. 注:这个生成的token 浏览器可以截获,不修改,然后模拟发送请求,不能避免。
  4. jwt实现了,数据不在后端,但是比cookie更安全。

  5. access token默认只有5分钟有效。

  6. 浏览器大部分可以看到荷载,但是看不到第一阶段的数据(盐)

  7. django中的盐默认在setting中

    SECRET_KEY = "django-insecure-b_$m3)zmrq5sy&j_4l&qwylk%&-bkn5k1!-a^!j_#*-!gw37!1"
    
-是一种前后端登陆认证的方案,区别于之前的 cookie,session
-它有 签发阶段--》登陆成功后签发
-它有 认证阶段--》需要登陆后才能访问的接口,通过认证后,才能继续操作

三段式 使用  . 去分割 
这三段分别代表什么?

# 下面这个就是jwt后的token

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ

base64编码

用途:

  1. 网络中传输
  2. 图片传输
  3. jwt使用

如何使用:

  1. 编码:
    1. 文字或者图片编码成二进制
    2. 使用base64.继续编码即可
  2. 解码:
    1. 使用base64.b64decode(二进制即可)
    2. 如果是文本,再转成文本即可。

base64编码一长度一定是4的倍数,如果不是用=号补齐,正常情况下=号最多3为,如果解不出可以尝试加=号,如果已经满足了,后面可以写N个等号。

编码文本

In [2]: import base64

In [3]: name = '小满'

In [4]: base64.b64encode(name.encode("utf-8"))
Out[4]: b'5bCP5ruh'

In [5]: base64.b64decode(b'5bCP5ruh').decode()
Out[5]: '小满'

编码图片

import base64

# 输入图片文件路径
input_path = r"C:\小满\Pictures\best.jpg"

# 输出的 base64 编码后的文本文件路径
output_path = "./结果.txt"

# 将图片文件编码为 base64 并写入到文本文件中
with open(input_path, "rb") as f1, open(output_path, "wb") as f2:
    while data := f1.read():
        # 编码为 base64
        encoded_data = base64.b64encode(data)
        # 将编码后的数据写入文本文件
        f2.write(encoded_data)

# 从 base64 编码的文本文件解码并写入到新的图片文件中
with open(output_path, "rb") as f1, open("./小满.jpg", "wb") as f2:
    while data := f1.read():
        # 解码 base64 数据
        decoded_data = base64.b64decode(data)
        # 将解码后的数据写入新的图片文件
        f2.write(decoded_data)

jwt开发重点

-签发token---》登陆接口
-校验token---》认证类

simplejwt

如何使用

  1. 登陆签发:默认使用auth的user表
  2. 只是测验的话,默认不需要视图层也可以测验。
# 自己写:登录 认证
# 第三方:方便快捷

# python的django框架中使用jwt
	- 安装
    - pip install djangorestframework-simplejwt
    
# 路由层
from rest_framework_simplejwt.views import token_obtain_pair

urlpatterns = [
    path("login/", token_obtain_pair)  # http:127.0.0.1:8000/login/  --> post请求 即可
]

# 视图层
from rest_framework.views import APIView
from rest_framework.mixins import CreateModelMixin


# 测试jwt
class LoginView(APIView, CreateModelMixin):
    queryset = Book.objects.all()
    serializer_class = BookSerializer
    

必须登录后才能发送post 比如新增一本图书 其他的视图(路由按上面的配置即可)

from rest_framework.permissions import IsAuthenticated  # 第一个模块
from rest_framework_simplejwt.authentication import JWTAuthentication  # 第二个模块



# 书籍 测试接口工具
# 127.0.0.1:8000/app01/book/
class BookViewSet(ModelViewSet):
    queryset = Book.objects.all()
    serializer_class = BookSerializer
    
     # 必须登录后才能新增
    authentication_classes = [JWTAuthentication] # 第一位
    permission_classes = [IsAuthenticated]  # 第二位 

image-20240418221031110

指定请求头

加了验证正常新增成功 注意 这里的key必须是 Authorization value必须加上 Bearer (可修改)的前缀

image-20240418221321364

image-20240418221311194

# refresh  刷新时间
# access  真正使用的
# refresh的过期时间更长一些,默认是7天
# access 过期时间更短一些,默认是1天
# 后续可以通过 refresh 去 更新 access 让用户无感刷新
{
    "refresh":  "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0b2tlbl90eXBlIjoicmVmcmVzaCIsImV4cCI6MTcxMzU3NzY0NywiaWF0IjoxNzEzNDkxMjQ3LCJqdGkiOiJiYzcwYjlkNGFiODM0NzdkOWIwOTEwMmQ0YmQ1OTNhYyIsInVzZXJfaWQiOjF9.GKGedTNKaFfnj_vJspKYrMOOf-29E0qYy_itErJAWPE",
    "access": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiZXhwIjoxNzEzNDkxNTQ3LCJpYXQiOjE3MTM0OTEyNDcsImp0aSI6IjQ4OWUyMTU3ZWY1MTQ5ZGNiOGEyOTI1M2Y5OTA0ZmZkIiwidXNlcl9pZCI6MX0.Uo16D_HHKQTkTGymQcNsdig3kFknr8D2Ox52vmtqUyQ"
}

JWT国际化

同drf一样,记得注册app,app的名字是

JWT的默认配置

# 通常情况下,只需要修改几个配置即可,把下面的配置,直接复制到settings里面
from datetime import timedelta

SIMPLE_JWT = {
     # Access Token的有效期
    'ACCESS_TOKEN_LIFETIME': timedelta(minutes=5), 
    
     # Refresh Token的有效期
    'REFRESH_TOKEN_LIFETIME': timedelta(days=7), 
    
    # 用于指定JWT在HTTP请求头中使用的身份验证方案。默认为"Bearer"
    "AUTH_HEADER_TYPES": ("Token",),  
    
     # 用于生成访问令牌和刷新令牌的序列化器。 改成你自己的即可
    "TOKEN_OBTAIN_SERIALIZER": "rest_framework_simplejwt.serializers.TokenObtainPairSerializer",
}

下面是默认配置

# JWT配置
SIMPLE_JWT = {
    'ACCESS_TOKEN_LIFETIME': timedelta(minutes=5),  # Access Token的有效期
    'REFRESH_TOKEN_LIFETIME': timedelta(days=7),  # Refresh Token的有效期
    
    # 对于大部分情况,设置以上两项就可以了,以下为默认配置项目,可根据需要进行调整
    
    # 是否自动刷新Refresh Token
    'ROTATE_REFRESH_TOKENS': False,  
    # 刷新Refresh Token时是否将旧Token加入黑名单,如果设置为False,则旧的刷新令牌仍然可以用于获取新的访问令牌。需要将'rest_framework_simplejwt.token_blacklist'加入到'INSTALLED_APPS'的配置中
    'BLACKLIST_AFTER_ROTATION': False,  
    'ALGORITHM': 'HS256',  # 加密算法
    'SIGNING_KEY': settings.SECRET_KEY,  # 签名密匙,这里使用Django的SECRET_KEY
    # 如为True,则在每次使用访问令牌进行身份验证时,更新用户最后登录时间
    "UPDATE_LAST_LOGIN": False, 
    # 用于验证JWT签名的密钥返回的内容。可以是字符串形式的密钥,也可以是一个字典。
    "VERIFYING_KEY": "",
    "AUDIENCE": None,# JWT中的"Audience"声明,用于指定该JWT的预期接收者。
    "ISSUER": None, # JWT中的"Issuer"声明,用于指定该JWT的发行者。
    "JSON_ENCODER": None, # 用于序列化JWT负载的JSON编码器。默认为Django的JSON编码器。
    "JWK_URL": None, # 包含公钥的URL,用于验证JWT签名。
    "LEEWAY": 0, # 允许的时钟偏差量,以秒为单位。用于在验证JWT的过期时间和生效时间时考虑时钟偏差。
    # 用于指定JWT在HTTP请求头中使用的身份验证方案。默认为"Bearer"
    "AUTH_HEADER_TYPES": ("Bearer",), 
    # 包含JWT的HTTP请求头的名称。默认为"HTTP_AUTHORIZATION"
    "AUTH_HEADER_NAME": "HTTP_AUTHORIZATION", 
     # 用户模型中用作用户ID的字段。默认为"id"。
    "USER_ID_FIELD": "id",
     # JWT负载中包含用户ID的声明。默认为"user_id"。
    "USER_ID_CLAIM": "user_id",
    
    # 用于指定用户身份验证规则的函数或方法。默认使用Django的默认身份验证方法进行身份验证。
    "USER_AUTHENTICATION_RULE": "rest_framework_simplejwt.authentication.default_user_authentication_rule",
    #  用于指定可以使用的令牌类。默认为"rest_framework_simplejwt.tokens.AccessToken"。
    "AUTH_TOKEN_CLASSES": ("rest_framework_simplejwt.tokens.AccessToken",),
    # JWT负载中包含令牌类型的声明。默认为"token_type"。
    "TOKEN_TYPE_CLAIM": "token_type",
    # 用于指定可以使用的用户模型类。默认为"rest_framework_simplejwt.models.TokenUser"。
    "TOKEN_USER_CLASS": "rest_framework_simplejwt.models.TokenUser",
    # JWT负载中包含JWT ID的声明。默认为"jti"。
    "JTI_CLAIM": "jti",
    # 在使用滑动令牌时,JWT负载中包含刷新令牌过期时间的声明。默认为"refresh_exp"。
    "SLIDING_TOKEN_REFRESH_EXP_CLAIM": "refresh_exp",
    # 滑动令牌的生命周期。默认为5分钟。
    "SLIDING_TOKEN_LIFETIME": timedelta(minutes=5),
    # 滑动令牌可以用于刷新的时间段。默认为1天。
    "SLIDING_TOKEN_REFRESH_LIFETIME": timedelta(days=1),
    # 用于生成访问令牌和刷新令牌的序列化器。
    "TOKEN_OBTAIN_SERIALIZER": "rest_framework_simplejwt.serializers.TokenObtainPairSerializer",
    # 用于刷新访问令牌的序列化器。默认
    "TOKEN_REFRESH_SERIALIZER": "rest_framework_simplejwt.serializers.TokenRefreshSerializer",
    # 用于验证令牌的序列化器。
    "TOKEN_VERIFY_SERIALIZER": "rest_framework_simplejwt.serializers.TokenVerifySerializer",
    # 用于列出或撤销已失效JWT的序列化器。
    "TOKEN_BLACKLIST_SERIALIZER": "rest_framework_simplejwt.serializers.TokenBlacklistSerializer",
    # 用于生成滑动令牌的序列化器。
    "SLIDING_TOKEN_OBTAIN_SERIALIZER": "rest_framework_simplejwt.serializers.TokenObtainSlidingSerializer",
    # 用于刷新滑动令牌的序列化器。
    "SLIDING_TOKEN_REFRESH_SERIALIZER": "rest_framework_simplejwt.serializers.TokenRefreshSlidingSerializer",
}

定制返回格式

默认的格式

image-20240419181556733

指定返回默认格式

# 1. 新建一个序列化类,比如 serializer

# 2. 在serializer中
# 导入模块
from rest_framework_simplejwt.serializers import TokenObtainPairSerializer

# 写一个类 继承  TokenObtainPairSerializer
class JWTSerializer(TokenObtainPairSerializer):
    def validate(self, attrs):
        dic = super().validate(attrs)  # 这里取到的就是一个字典 包含refresh和access
        data = {
            "code": 100,
            "msg": "登录成功",
            "username": self.user.username,  # 这个就是用户对象
            "refresh": dic.get("refresh"),
            "access": dic.get("access")
        }

        # 定制号直接返回,因为源码中帮我们处理过异常了,所以不需要再次处理
        return data
   

# 3. 在settings中,配置成我们自己的就行
SIMPLE_JWT = {
     # 用于生成访问令牌和刷新令牌的序列化器。 改成你自己的即可
    "TOKEN_OBTAIN_SERIALIZER": "app01.lib.serializer.JWTSerializer",
}

# 路由层
from django.urls import path, include
from rest_framework.routers import DefaultRouter
from rest_framework_simplejwt.views import token_obtain_pair

urlpatterns = [
    path("login/", token_obtain_pair)
]
# 前端直接访问 http://127.0.0.1:8000/app01/login/

image-20240419182643717

定制payload格式

荷载中包含用户名的情况比较少,不过这个案例中尝试在荷载中加一个用户名。

# 步骤和指定返回格式一模一样,只是多了一个东西
# 重写get_token即可

# 定制返回格式
class JWTSerializer(TokenObtainPairSerializer):
    
    @classmethod
    def get_token(cls, user):
        username = user.username
        # 对象调用类的绑定方法,会自动把对象的类传入
        # token对象,当字典用 看源码
        token = super().get_token(user)
        token['username'] = username
        return token
    
    def validate(self, attrs):
        dic = super().validate(attrs)
        data = {
            "code": 100,
            "msg": "登录成功",
            "username": self.user.username,  # 这个就是用户对象
            "refresh": dic.get("refresh"),
            "access": dic.get("access")
        }

        # 定制号直接返回,因为源码中帮我们处理过异常了,所以不需要再次处理
        return data
# 之前的
{"token_type":"access","exp":1713522991,"iat":1713522691,"jti":"63dbcb48fadb480886cec39010a69e4c","user_id":1}

# 我们添加用户名的
{"token_type":"access","exp":1713524715,"iat":1713524415,"jti":"16c31123e07940fb85c8bcb0c382a52c","user_id":1,"username":"admin"}

多方式登录(基于auth的user表)

  1. 默认的auth的user表,只能传用户名,密码校验
  2. 但是项目中,一般是 手机号/用户名/邮箱 + 密码 的方式去登录。
    1. 这个时候,使用simple_jwt就不行了,因为它只能校验用户名和密码
  3. 所以需要扩写user表,实现多方式登录。-->还是使用auth的user表,增加字段。
    1. 签发token:我们自己签发
    2. 认证:继续使用simple_jwt的认证即可
  4. 编写一个多方式登录接口
    1. 扩写auth的user表-->加入mobile字段
      1. 坑:
        1. 之前迁移过-->auth的user表已经生成了,就不能扩写了
          1. 方案1,创建新项目,从头做
          2. 删库,删除迁移记录(咱们自己app和django自己的迁移记录)删除auth和amdin的迁移记录自己的需要去源码里面删除。
        2. 所以,如果要扩写auth的user表,必须在迁移之前就定好,写好。

image-20240419195428959

image-20240419195705275

# 视图层
# 在此视图上增加mobile,登录成功我们签发token,simple_jwt帮我们做验证
class JWTView(GenericViewSet):
    # 因为我们这里只是做反序列化,不会给前端表中的书籍,所以这里不写都可以
    queryset = None # 
    serializer_class = LoginSerializer
    @action(methods=["POST"], detail=False)
    def login(self, request):
        # 正常逻辑:  取出手机号/邮箱/用户名 去数据库进行校验, 校验通过 签发token 返回给前端
        # 高级逻辑:  上述逻辑去序列化类做 --> 更精简
        serializer = self.get_serializer(data=request.data)
        if serializer.is_valid(): # 执行三层认证  自己的 局部钩子 全局钩子
            # 校验通过,返回给前端
            # 现在在视图类中  有一个序列化类 ——-》如果要把序列化类中的变量给视图,或者视图类中的变量给序列化类,需要借助——-》context
            # context 就是一个桥梁  [字典]
            # 如果要把request传递过去,上面写context={"request": request}即可
            refresh = serializer.context.get("refresh")
            access = serializer.context.get("access")
            token = serializer.context.get("token")
            return Response({"code": 100, "msg": "登录成功", "refresh": refresh, "access": access}, headers={"token": token})
        else:
            return Response({"code": 101, "msg": serializer.errors})
# 序列化层
import re
from rest_framework_simplejwt.tokens import RefreshToken
from rest_framework_simplejwt.serializers import TokenObtainPairSerializer
from rest_framework.exceptions import ValidationError
from app01.models import Book, UserInfo

# 登录序列化类校验
class LoginSerializer(TokenObtainPairSerializer):
    username = serializers.CharField()  # 可能是用户名 邮箱 手机号
    password = serializers.CharField()
    
    # 逻辑封装到函数里面
    def _get_user(self, attrs):
        # 获取用户名和密码
        username = attrs.get("username", None)
        password = attrs.get("password", None)
        
        # 去数据库查询 username可能是手机号 邮箱 用户名 需要通过邮箱去匹配
        if re.match(r"^1[3-9]\d{9}$", username):
            user = UserInfo.objects.filter(mobile=username).first()
        elif re.match(r".*?@.*[com|cn|net]", username):
            user = UserInfo.objects.filter(email=username).first()
        else:
            user = UserInfo.objects.filter(username=username).first()
        
        if user and user.check_password(password):
            return user
        else:
            raise ValidationError("用户名或密码错误")
        
    
    # 从函数取出校验后的书籍,让代码更精简
    def validate(self, attrs):
        user = self._get_user(attrs)
        # 签发token 通过user去签发
        # 这里直接拿到的token是refersh的
        token = RefreshToken.for_user(user)
        self.context['refresh'] = str(token)
        self.context['access'] = str(token.access_token)
        self.context['token'] = str(token)
        return attrs
# 路由
from django.urls import path, include
from rest_framework.routers import DefaultRouter
from rest_framework_simplejwt.views import token_obtain_pair
from .views import JWTView

router = DefaultRouter()
router.register("jwt", JWTView, basename="jwt")

urlpatterns = [
    path("", include(router.urls)),
    path("login/", token_obtain_pair)  # http://127.0.0.1:8000/app01/jwt/login/
]

总结

# 1 校验数据,放到序列化类的 validate中,而不放在视图类的方法中乐
# 2 视图类和序列化类直接交互变量
	serializer.context
    
# 3 user.check_password  必须是auth的user表,校验密码使用它
# 4 attrs必须返回值,返回空报错
# 5 视图类的方法校验失败的else中:也要return Response
# 6 如何签发token
token = RefreshToken.for_user(user)
self.context['access'] = str(token.access_token)
self.context['refresh'] = str(token)

自定义用户表,手动签发和认证

# 视图层
class JWTOurView(GenericViewSet):
    serializer_class = LoginOurSerializer
    
    @action(methods=["POST"], detail=False)
    def login(self, request):
        # 正常逻辑:  取出手机号/邮箱/用户名 去数据库进行校验, 校验通过 签发token 返回给前端
        # 高级逻辑:  上述逻辑去序列化类做
        serializer = self.get_serializer(data=request.data)
        if serializer.is_valid(): # 执行三层认证  自己的 局部钩子 全局钩子
            refresh = serializer.context.get("refresh")
            access = serializer.context.get("access")
            token = serializer.context.get("token")
            return Response({"code": 100, "msg": "登录成功", "refresh": refresh, "access": access}, headers={"token": token})
        else:
            return Response({"code": 101, "msg": serializer.errors})
# 序列化类
from rest_framework import serializers
from rest_framework_simplejwt.serializers import TokenObtainPairSerializer
from rest_framework.exceptions import ValidationError
from app01.models import User
from rest_framework_simplejwt.tokens import RefreshToken

class LoginOurSerializer(TokenObtainPairSerializer):
    username = serializers.CharField()  
    password = serializers.CharField()
    
    # 逻辑全部写这里
    def _get_user(self, attrs):
        # 获取用户名和密码
        username = attrs.get("username", None)
        password = attrs.get("password", None)
        
        # 去数据库查询 username可能是手机号 邮箱 用户名 需要通过邮箱去匹配
        if re.match(r"^1[3-9]\d{9}$", username):
            user = UserInfo.objects.filter(mobile=username).first()
        elif re.match(r".*?@.*[com|cn|net]", username):
            user = UserInfo.objects.filter(email=username).first()
        else:
            user = UserInfo.objects.filter(username=username).first()
        
        if user and user.check_password(password):
            return user
        else:
            raise ValidationError("用户名或密码错误")
        
    
    # 这里都不需要修改
    def validate(self, attrs):
        user = self._get_user(attrs)
        token = RefreshToken.for_user(user)
        self.context['refresh'] = str(token)
        self.context['access'] = str(token.access_token)
        self.context['token'] = str(token)
        return attrs

自定义用户认证类

# 模型层
class Book(models.Model):
    name = models.CharField(max_length=32, verbose_name="图书名称")
    price = models.IntegerField(verbose_name="图书价格")
    publish = models.CharField(max_length=32, verbose_name="出版社")
    
    class Meta:
        db_table = "book"
# 自定义认证类
from rest_framework_simplejwt.authentication import JWTAuthentication
from app01.models import User
from rest_framework.exceptions import AuthenticationFailed

class JWTBaseAuthentication(JWTAuthentication):
    def authenticate(self, request):
       token = request.META.get("HTTP_AUTHORIZATION")

       if token: 
            # 这里返回的是payload数据,是可以信任的payload,所以可以从这里直接拿取到用户的id  user_id
            get_validated_token = self.get_validated_token(token)
            user_id = get_validated_token['user_id']
            user = User.objects.get(pk=user_id)
            
            return user, token
       else:
           raise AuthenticationFailed("请携带登录信息")
# 视图
from rest_framework.viewsets import  ModelViewSet
from app01.lib.authentication import JWTBaseAuthentication

class BookView(ModelViewSet):
    # 不登录无法操作
    authentication_classes = [JWTBaseAuthentication]
    
    queryset = Book.objects.all()
    serializer_class = serializer.BookSerializer

image-20240419233224707

image-20240419233254863

posted @ 2024-04-18 23:02  小满三岁啦  阅读(15)  评论(0编辑  收藏  举报