drf之jwt

昨日回顾

1 过滤
    -内置过滤类SearchFilter,配置search_fields,指定过滤的字段,可以模糊匹配,?search=搜索条件
    -自定义过滤类:写一个类,继承BaseFilterBackend,重写filter_queryset方法,方法返回qs对象,就是过滤后的对象,配置在视图类中即可
    -django-filter,导入DjangoFilterBackend,配置在视图类上,配置filter_fields=['name','publish'],指定过滤字段,不能模糊匹配,?name=搜索条件


2 排序
    -内置排序类OrderingFilter,配置ordering_fields,指定排序的字段,可以实现排序,?ordering=-price

3 过滤和排序可以连用---》尽量把过滤写在前面

4 分页:三种分页方式
    PageNumberPagination:用的多,传统的分页 page=第几页&size=每页多少条
    LimitOffsetPagination:从第几条开始offset=第几条,取几条limit=取几条
    CursorPagination:只能选择看上一页或下一页,效率高

    重写某个分页类,修改某几个类属性后,把分页类,配置在视图类上:pagination_class=分页类

5 接口文档编写
    -前后端分离的项目,前端和后端是两拨人写
    -纯手写(word,md)---》git
    -第三方平台(收费,免费),在平台录入
    -公司自己搭建(公司自己开发),录入,直接批量导入
    -补充:postman测试接口
    -自动生成接口文档(coreapi,swagger)

今日内容

0 全局异常处理

# 无论访问接口是否正常,都返回统一的格式
# drf内置有统一的异常处理(认证,失败抛异常),APIException及其子类的已经处理了,但是其他异常没有处理
# from rest_framework.views import exception_handler 就是drf的异常处理,不满足咱们的需求,无论什么异常,都统一处理,重写一个函数

# drf默认的异常处理
exception_handler

自定义异常处理

common.py

# 重写exception_handler方法
from rest_framework.views import exception_handler
from rest_framework.response import Response


def common_exception_handler(exc, context):
    # res如果是Response,表示是drf的异常,并且处理好了,如果是None,表示其他异常,并没有处理

    res = exception_handler(exc, context)  # 这个函数的执行,会处理drf的异常
    # 重点,要在这里记录日志(程序出错了,才会走到这)
    if res:
        return Response(data={'code': 888, 'msg': res.data.get('detail')})
    else:
        return Response(data={'code': 999, 'msg': '服务器内部错误,请联系系统管理员'})

settings.py

REST_FRAMEWORK = {
    # 这样配置,一旦出了异常,就会走这个函数,exception_handler内置的不满足咱们的的需求
    'EXCEPTION_HANDLER': 'app01.common.common_exception_handler',
}

1 jwt介绍和本质原理

Json web token (JWT), 用来做登录认证的。
回顾cookie,session,token

jwt构成:JWT就是一段字符串,由三段信息构成的,将这三段信息文本用.链接一起就构成了Jwt字符串
样子(每一段都用base64转码了):eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ
# 三部分:
    第一部分我们称它为头部(header):公司信息,加密方式
    第二部分我们称其为载荷(payload):当前登录用户的信息:id,name,过期时间
    第三部分是签证(signature):把前两段通过加密得到的串



# 签发和认证
    -用户携带用户名密码登录---》用户名密码是正确的---》签发token,返回给前端
    -用户访问需要登录后才能访问的接口,携带token过来,认证过后,才能允许访问

# base64的编码和解码很常用
通常在网络传输中使用,甚至你能看到,有的图片,使用了base64转码

base64的编码
import base64
import json
dic = {'name':lqz,'age':19}
dic_str = json.dumps(dic)
print(dic_str)
res = base64.b64encode(dic_str.encode('utf-8'))
print(res)
# base64的编码,都是4的倍数,如果不够,用=补全

base64的解码
res = base64.b64decode('xxx')

2 drf-jwt的快速使用

# 使用第三方模块,快速签发和认证token
# pip3 install djangorestframework-jwt

2.1 快速签发

from rest_framework_jwt.views import obtain_jwt_token
# 在路由中配置,创建一个超级用户即可
path('login/', obtain_jwt_token),# 签发token是基于auth的user表签发的,前端需要携带用户密码发送post请求到login/,如果用户密码正确,就登录成功,并且获得token,如果失败,就报用户名或密码错误

2.2 认证token

# 只需要在视图类中配置认证类和权限类
class TestView(APIView):
    # 只写这一个认证类,如果没有携带token,并不会限制,需要搭配一个权限类,来完成登录认证
    authentication_classes = [JSONWebTokenAuthentication,]
    permission_classes = [IsAuthenticated,]


# 访问的时候,必须把token放到请求头中
key:Authorization
valeu:JWT eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoxLCJ1c2VybmFtZSI6ImFkbWluIiwiZXhwIjoxNjQ0MjkwNDQwLCJlbWFpbCI6IjNAcXEuY29tIn0.FxsDGHQg-aXV7Svt3yRk0mjawH71fa_LYXo77N0OWM

3 drf-jwt修改签发响应格式

# 写一个函数,函数的返回值,就是响应的格式# 在配置文件中配置一下def jwt_response_payload_handler(token, user=None, request=None):    return {        'code': 100,        'msg': '登录成功',        'token': token,        'username':user.username    }在配置文件中配置JWT_AUTH = {    'JWT_RESPONSE_PAYLOAD_HANDLER': 'app01.common.jwt_response_payload_handler',}

4 drf-jwt自定义用户表签发token

from rest_framework_jwt.settings import api_settings
jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER
class LoginView(APIView):
    def post(self, request):
        response = {'code': 101, 'msg': '用户名或密码错误'}
        username = request.data.get('username')
        password = request.data.get('password')
        user = User.objects.filter(username=username, password=password).first()
        if user:
            # 登录成功,签发token,通过当前登录用户获取荷载(payload)
            payload = jwt_payload_handler(user)
            # 通过payload生成token串(三段:头,payload,签名)
            token = jwt_encode_handler(payload)

            response['code'] = 100
            response['msg'] = '登录成功'
            response['token'] = token

        return Response(response)

5 drf-jwt自定义认证类

# 自定义认证类
from rest_framework.authentication import BaseAuthentication
from rest_framework.exceptions import AuthenticationFailed
from rest_framework_jwt.settings import api_settings

jwt_decode_handler = api_settings.JWT_DECODE_HANDLER
import jwt
from .models import User

class JwtAuthentication(BaseAuthentication):
    def authenticate(self, request):
        # 第一步:取出前端传入的token串(从请求头的token中取出)
        jwt_value = request.META.get('HTTP_TOKEN')
        if jwt_value:
            # 验证token是否合法
            try:
                # 通过token,得到payload,在得到的过程中会校验,是否过期,是否被穿该
                payload = jwt_decode_handler(jwt_value)
            except jwt.ExpiredSignature:
                raise AuthenticationFailed('签名过期')
            except jwt.DecodeError:
                raise AuthenticationFailed('签名验证失败')
            except jwt.InvalidTokenError:
                raise AuthenticationFailed('未知错误')

            print(payload)
            # 有没有问题?每次都要去数据库查用户,效率低
            # 优先用这种
            user=User.objects.get(id=payload['user_id'])

            # 不需要查数据库,效率高,存在缺陷,
            # user=User(id=payload['user_id'],username=payload['username'])
            return (user, jwt_value)

        else:
            # 没带token串,没登录
            raise AuthenticationFailed('您没有携带token')



# 局部使用,在视图类中配置
from .auth import JwtAuthentication
class TestView(APIView):
    # 自己写的认证类
    authentication_classes = [JwtAuthentication, ]

    def get(self, request):
        print(request.user) # 这就是当前登录用户
        return Response('你必须登录,才能看到我')

6 局部使用和全局使用

# 全局使用,在配置文件中配置
REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES':['app01.auth.JwtAuthentication'], # 全局生效

}
# 全局使用后,局部禁用
class LoginView(APIView):
    authentication_classes = []


# 注意:配置的所有认证,权限,频率。。。优先用视图类自己的,再用配置文件的,最后用drf内置的



# 补充:token过期时间很快,改改过期时间,配置7天过期
JWT_AUTH = {
    'JWT_RESPONSE_PAYLOAD_HANDLER': 'app01.common.jwt_response_payload_handler',
    # 过期时间7天
    'JWT_EXPIRATION_DELTA': datetime.timedelta(days=7),
}

posted @ 2022-02-15 21:35  一叶松  阅读(158)  评论(0编辑  收藏  举报