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),
}