drf-jwt、simplejwt的使用

发布时间 2023-04-24 20:55:34作者: ERROR404Notfound

1.接口文档

# 前后端分离
	-我们做后端,写接口
    -前端做前端,根据接口写app,pc,小程序
    
    -作为后端来讲,我们很清楚,比如登录接口  /api/v1/login/---->post---->username,password 编码方式json----》返回的格式  {code:100,msg:登录成功}
    
    
    -后端人员,接口写完,一定要写接口文档
    
    
# 接口文档如何编写
	1 使用word,md 编写接口文档
	2 使用第三方平台,编写我们的接口文档(非常多)---》收费
    	-https://www.showdoc.com.cn/item/index
	3 公司自己使用第三方开源的搭建的---》Yapi ---》你如果想自己搭建
    	-https://zhuanlan.zhihu.com/p/366025001 
            
	4 使用drf编写的接口,可以自动生成接口文档
    	-swagger---》drf-yasg---》官方推荐使用
        -coreapi----》咱们讲
    
    
# 使用coreapi自动生成接口文档步骤
	- 1 安装 
    - 2 配置路由
        from rest_framework.documentation import include_docs_urls
        path('docs/', include_docs_urls(title='xx项目接口文档')),
    -3 在视图类,方法上,写注释即可
    	-在类上加注释
        -在类的方法上加注释
        -在序列化类或表模型的字段上加  help_text,required。。。。
    
    
    -4 配置文件配置
    	REST_FRAMEWORK = {
     		'DEFAULT_SCHEMA_CLASS': 'rest_framework.schemas.coreapi.AutoSchema',

    	}
        
        
   -5 访问地址:http://127.0.0.1:8000/docs
# 1    
    
    
# 接口文档,需要有的东西
	-描述
    -地址
    -请求方式
    -请求编码格式
    -请求数据详解(必填,类型)
    -返回格式案例
    -返回数据字段解释
    -错误码

2.jwt介绍和原理

1.在用户注册或登陆后,我们想记录用户的登录状态,或者为用户创建身份认证的凭证。我们不再使用Session认证机制,而是使用Json Web Token(本质就是token)认证机制。
"""
Json web token (JWT), 是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准((RFC 7519).该token被设计为紧凑且安全的,特别适用于分布式站点的单点登录(SSO)场景。JWT的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源,也可以增加一些额外的其它业务逻辑所必须的声明信息,该token也可直接被用于认证,也可被加密。
"""

2.JWT的构成:
	JWT是一段字符串,由三段信息构成,将这三段信息文本用.连接在一起,就构成了JWT字符串,例如:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ
第一段信息:
	头(header),声明加密算法和类型:
import base64
import json
res = base64.b64decode('eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9')
print(json.loads(res))  # {'alg': 'HS256', 'typ': 'JWT'}      

第二段信息:
	荷载(payload):存放有效信息的地方:过期时间、签发时间、用户id、用户名等。
标准中注册的声明 (建议但不强制使用) :

iss: jwt签发者
sub: jwt所面向的用户
aud: 接收jwt的一方
exp: jwt的过期时间,这个过期时间必须要大于签发时间
nbf: 定义在什么时间之前,该jwt都是不可用的.
iat: jwt的签发时间
jti: jwt的唯一身份标识,主要用来作为一次性token,从而回避时序攻击。
            
第三段信息:
	签名(signature),这个方式需要base64加密后的header和base64加密后的payload用.连接起来组成字符串,然后通过header中声名的加密方式进行加盐secret组合加密,就构成了jwt的第三部分。
// javascript
var encodedString = base64UrlEncode(header) + '.' + base64UrlEncode(payload);

var signature = HMACSHA256(encodedString, 'secret'); // TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ(得到的仅是第三部分。
"""
注意:secret是保存在服务端当中的,并且服务端只有一个secret,也就是所有的用户加密的秘钥都是相同的,jwt的签发也是在服务端发生,secret就是用来进行jwt的签发和jwt的验证,所以这是服务端的私钥,任何时候都不应该泄露出去。泄露出去代表客户端可以自己签发jwt了。
"""  

3.drf-jwt的快速使用

1.django+drf平台开发jwt有两个模块:
djangorestframework-jwt(用的时间比较久了)
djangorestframework-simplejwt(公司使用较多 )

2.djangorestframework-jwt使用:
安装:
	pip install djangorestframework-jwt
使用:
	默认签发token都是根据auth_user表签发的,如果自定义表格,需要自己手动编写代码。
代码:
# 导入模块
from rest_framework_jwt.views import obtain_jwt_token
urlpatterns = [
    path('login/',obtain_jwt_token) # 添加路由,此后向/login/这个路由发送post申请,并且携带{"username":xxx,"password":xxx}就可以登陆获得token
]
并且在auth_user中插入一条用户数据。

4.定制返回信息

此后,基于auth的User表签发token就可以不用自己写了,但是登陆接口返回对的格式只有token,不符合公司规定。通过以下方式添加信息:
在app中建一个py文件:jwt_response_payload_handler,在里面新建一个函数:jwt_response_payload_handler。
def jwt_response_payload_handler(token,user=None,request=None):
    return {
        'code':100,
        'msg':'登录成功',
        'token':token,
        'username':user.username
    }
在settings.py中配置:
JWT_AUTH = {
    'JWT_RESPONSE_PAYLOAD_HANDLER': 'app01.jwt_response_payload_handler.jwt_response_payload_handler',
}

5.jwt认证类

很多接口需要登陆以后才能访问,我们需要在视图类上加一个认证类和一个权限类。
代码:
views.py:
from rest_framework_jwt.authentication import JSONWebTokenAuthentication
from rest_framework.permissions import IsAuthenticated

class BookView(ViewSetMixin,APIView):
    # 首先需要加两个认证类:
    authentication_classes = [JSONWebTokenAuthentication]  # 校验是否携带token
    permission_classes = [IsAuthenticated]  # 校验是否登陆
    def list(self,request):
        """
        查询所有图书接口
        """
        books = Book.objects.all()
        paginator = CommonLimitOffsetPagination()
        page = paginator.paginate_queryset(books,request,self)
        if page is not None:
            serializer = BookSerializer(instance=page,many=True)
            return Response({
                'total':paginator.count,
                'next':paginator.get_next_link(),
                'previous':paginator.get_previous_link(),
                'results':serializer.data
            })
 然后在发送get请求时,需要在请求头中携带键值对:Authorization:jwt 空格 token,即可发送成功。

6.simplejwt使用

1.djangorestframework-simplejwt是一个模块,使用前要先下载。

2.settings.py需要配置以下内容:
INSTALLED_APPS = [
'rest_framework',
]
REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': [
        'rest_framework_simplejwt.authentication.JWTAuthentication',  # 使用rest_framework_simplejwt(token)验证身份
        'rest_framework.authentication.SessionAuthentication',  # 基于用户名密码认证方式
        'rest_framework.authentication.BasicAuthentication'  # 基于Session认证方式
    ],
    'DEFAULT_PERMISSION_CLASSES': [
        'rest_framework.permissions.IsAuthenticated'  # 默认权限为验证用户
    ],
}

import datetime
SIMPLE_JWT = {
    # token有效时长
    'ACCESS_TOKEN_LIFETIME': datetime.timedelta(minutes=30),
    # token刷新后的有效时间
    'REFRESH_TOKEN_LIFETIME': datetime.timedelta(days=1),
}

urls.py:
from django.contrib import admin
from django.urls import path,include
from app01 import views

from rest_framework_simplejwt.views import (
    TokenObtainPairView,
    TokenRefreshView,
    TokenVerifyView
)

urlpatterns = [
    path('admin/', admin.site.urls),
    path('api/auth/', include('rest_framework.urls', namespace='rest_framework')),
    # 获取Token的接口
    path('api/token/', TokenObtainPairView.as_view(), name='token_obtain_pair'),
    # 刷新Token有效期的接口
    path('api/refresh/', TokenRefreshView.as_view(), name='token_refresh'),
    # 验证Token的有效性
    path('api/token/verify/', TokenVerifyView.as_view(), name='token_verify'),
    path('books/',views.BookView.as_view())
    ]

models.py:
from django.db import models

class Book(models.Model):
    name = models.CharField(max_length=32)
    price = models.CharField(max_length=32)
    publish = models.ForeignKey(to='Publish', on_delete=models.CASCADE)
    authors = models.ManyToManyField(to='Author')

    def publish_detail(self):
        return {'name': self.publish.name,'addr':self.publish.addr}

    def author_list(self):
        l = []
        for author_obj in self.authors.all():
            l.append({'name':author_obj.name,'phone':author_obj.phone})
        return l

class Publish(models.Model):
    name = models.CharField(max_length=32)
    addr = models.CharField(max_length=32)

class Author(models.Model):
    name = models.CharField(max_length=32)
    phone = models.CharField(max_length=11)

serializer.py:
from rest_framework import serializers
from app01.models import Book
from rest_framework.exceptions import ValidationError

class BookSerializer(serializers.ModelSerializer):
    class Meta:
        model = Book
        fields = ['name', 'price', 'publish', 'authors', 'publish_detail', 'author_list']
        extra_kwargs = {
            'name': {'max_length': 8},
            'publish_detail': {'read_only': True},
            'author_list': {'read_only': True},
            'publish': {'write_only': True},
            'authors': {'write_only': True}
        }

        def validate_name(self, name):
            if name.startswith('s'):
                raise ValidationError('书名不能以s开头')
            else:
                return name
views.py:
from rest_framework.generics import GenericAPIView
from rest_framework.mixins import CreateModelMixin,ListModelMixin
from rest_framework.permissions import IsAuthenticated
from .models import Book
from .serializer import BookSerializer

class BookView(GenericAPIView,ListModelMixin):
    queryset = Book.objects.all()
    serializer_class = BookSerializer
    # simplejwt只需要配置权限类
    permission_classes = [IsAuthenticated]
    def get(self,request):
        return self.list(request)
    
3.执行数据库迁命令:makemigrate、migrate。再执行创建管理员操作:createsuperuser。
      
4.获取token:
路由:http://127.0.0.1:8000/api/token/。获取路由使用post请求并且需要在body当中携带用户名和密码。获取到的token有两个,一个是refresh,一个是access。refresh是用来刷新的,accrss是用来查数据的。

5.刷新token:
路由:http://127.0.0.1:8000/api/refresh/。刷新token时需要携带老的refresh token。

6.查数据:
路由:http://127.0.0.1:8000/books/。由于在视图函数中已经配置了权限类,所以token校验通过之后才会走。并且需要在请求头当中携带token,携带格式为:{'Authorization':'Bearer token字符串'}