drf(JWT认证)
一. jwt实现过程
1. 构建jwt过程
第一: 用户提交用户名和密码给服务端,如果登录成功,使用jwt创建一个token,并给用户返回
eyJ0eXAiOiJqd3QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoxLCJ1c2VybmFtZSI6InpjYyIsImV4cCI6MTU5NDczODg5MX0.OCG4mUhs_yXIkxtxvG9MWJWjpbvnSGDcqMVtpsn_0mo
第二步: 构建三段字符串之间的关系
# 第一段字符串 headers内部包含了算法 和 token类型。
流程: 先将python类型对象装换成json格式字符串, 然后做base64加密
headers = {
'typ': 'jwt',
'alg': 'HS256',
} # 第二段字符串payload,自定义的值
流程: 先将python类型对象装换成json格式字符串,然后做base64加密
payload = {
'user_id': user.pk,
'username': username,
'exp': datetime.datetime.utcnow() + datetime.timedelta(seconds=300), # 超时时间
} # 第三段字符串
第一步:把1,2部分base64加密过后的结果进行拼接加密
第二步:对前2部分的加密结果进行hs256加密 + 加盐
第三步:对hs256加密后的密文在进行base64url加密再拼接到前1, 2部分base64格式的末尾作为sign.
第三步: 以后用户访问时,需要携带token,后端需要对token校验
2. 校验jwt过程
第一步: 获取token
第二步: 对token进行切割, 获取第二段内容进行base64解密,获取payload信息, 检查超时时间是否超时
第三步:由于第三部分的字符串不能反解,把第一和第二段在进行hs256加密
1. 把1,2部分base64的密文拼接加密
2. 对前2部分加密进行hs256加密+加盐得到密文
3. 再将密文机进行base64加密, 与前两段的base64d格式的密文进行对比, 如果相等,表示token没有修改通过.
二. drf-jwt安装
官网: http://getblimp.github.io/django-rest-framework-jwt/
安装: pip install djangorestframework-jwt
三. 使用内置jwt认证+签发token
1. 快速使用
urls.py
# 路由中配置
# 提示:
'''
obtain_jwt_token本质是由ObtainJSONWebToken类调用as_view类方法实例化出来的, 其实路由中这样写也可以:
path('login/', ObtainJSONWebToken.as_view()),
''' from rest_framework_jwt.views import ObtainJSONWebToken, obtain_jwt_token, JSONWebTokenAPIView, VerifyJSONWebToken urlpatterns = [
# path('login/', ObtainJSONWebToken.as_view()),
path('login/', obtain_jwt_token),
]
views.py
from rest_framework_jwt.authentication import JSONWebTokenAuthentication # Create your views here.
# 快速实现jwt
class BookAPIView(APIView):
authentication_classes = [JSONWebTokenAuthentication, ] def get(self, request):
return Response('OK')
解析: 为什么路由中配置了obtain_jwt_token用户认证, 签发token等等都不需要写了?
# 帮我们写了视图认证实现接受用户请求及基于请求响应:
看继承关系: obtain_jwt_token = ObtainJSONWebToken.as_view() -> ObtainJSONWebToken -> JSONWebTokenAPIView
JSONWebTokenAPIView就是我们的视图类. 它里面写了post方法, 处理我们的认证请求. # 帮我们写了序列化器实现了token的签发:
class ObtainJSONWebToken(JSONWebTokenAPIView):
# JSONWebTokenSerializer内部就在序列换器里面使用了validate钩子, 实现了token的签发
serializer_class = JSONWebTokenSerializer
使用内置提供的认证
头部访问格式: 使用内置的如果没有修改配置文件中配置的前缀, 那么jwt前缀必须要加, 如果不加前缀认证就返回None, 认证就失效了. 大小写都行.
Authorization | jwt eyJ0eXAiOiJqd3QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoxLCJ1c2VybmFtZSI6InlhbmciLCJleHAiOjE1OTUwODY3NDZ9.aaehvGOl3AMI5gfU2Z9L8GH015pWIitOCXLgBJ5zl8E |
---|---|
Authorization | JWT eyJ0eXAiOiJqd3QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoxLCJ1c2VybmFtZSI6InlhbmciLCJleHAiOjE1OTUwODY3NDZ9.aaehvGOl3AMI5gfU2Z9L8GH015pWIitOCXLgBJ5zl8E |
拓展: 认证前缀可以修改
from rest_framework_jwt import settings
'JWT_AUTH_HEADER_PREFIX': 'JWT',
rom rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.permissions import IsAuthenticated
from rest_framework_jwt.authentication import JSONWebTokenAuthentication # Create your views here.
# 使用jwt提供的认证类,局部使用 # 可以通过认证类JSONWebTokenAuthentication和权限类IsAuthenticated,来控制用户登录以后才能访问某些接口
# 如果用户不登录就可以访问,只需要把权限类IsAuthenticated去掉就可以
class Order(APIView):
authentication_classes = [JSONWebTokenAuthentication, ]
permission_classes = [IsAuthenticated, ] def get(self, request, *args, **kwargs):
return Response('这是订单信息')
3 使用内置认证控制登录成功时response返回的格式
utils.py
from rest_framework_jwt.utils import jwt_response_payload_handler def custom_jwt_response_payload_handler(token, user=None, request=None):
# 返回什么, 认证成功时就返回什么格式
return {
'status': 1000,
'messages': '登录成功',
'token': token,
'username': user.username
}
settings.py
# 第二步: settings.py文件中配置成自己的路径即可
JWT_AUTH = {
# utils.jwt_response_payload_handler.custom_jwt_response_payload_handler
'JWT_RESPONSE_PAYLOAD_HANDLER':
'utils.jwt_response_payload_handler.custom_jwt_response_payload_handler',
}
四. 自定义jwt认证+签发
1. 自定义jwt认证
1) 继承BaseAuthentication实现
utils.py
from rest_framework.authentication import BaseAuthentication
from rest_framework.exceptions import AuthenticationFailed
from rest_framework_jwt.authentication import jwt_decode_handler
from rest_framework_jwt.utils import jwt_decode_handler # 用上面的也可以
import jwt
from app01 import models class MyJwtAuthentication(BaseAuthentication):
def authenticate(self, request):
jwt_value = request.META.get('HTTP_AUTHORIZATION')
if jwt_value:
try:
payload = jwt_decode_handler(jwt_value)
except jwt.ExpiredSignature:
raise AuthenticationFailed('签名过期!')
except jwt.DecodeError:
raise AuthenticationFailed("签名解码错误!")
except jwt.InvalidTokenError:
raise AuthenticationFailed('token无效!')
except Exception as e:
raise AuthenticationFailed(str(e))
print(payload)
# {'user_id': 3, 'username': 'zd', 'exp': 1696772425, 'email': ''}
# 方式一: 缺点, 查数据库耗费时间
# user_obj = User.objects.get(pk=payload.get('user_id'))
# print('user_obj.phone:', user_obj.phone) # 17621839222 # 方式二: 缺点, 没有传递的数据就获取不到
user_obj = models.UserInfo(id=payload.get('user_id'), username=payload.get('username'))
print('user_obj.phone:', [user_obj.phone])
return user_obj, jwt_value # ['']
# 没有携带值,直接抛异常
raise AuthenticationFailed('没有携带认证信息')
views.py
from app02.utils import MyJwtAuthentication class Goods(APIView):
authentication_classes = [MyJwtAuthentication, ] def get(self, request, *args, **kwargs):
return Response('商品信息')
2) 继承BaseJSONWebTokenAuthentication + 手动get获取jwt_value 或者 自动获取jwt_value实现
utls.py
import jwt
from rest_framework_jwt.authentication import BaseJSONWebTokenAuthentication from rest_framework_jwt.authentication import jwt_decode_handler
from rest_framework_jwt.utils import jwt_decode_handler from rest_framework.exceptions import AuthenticationFailed
from rest_framework.exceptions import APIException from rest_framework.authentication import get_authorization_header class MyJwtAuthentication(BaseJSONWebTokenAuthentication):
def authenticate(self, request):
# 手动获取jwt_value
# jwt_value = request.META.get('HTTP_AUTHORIZATION')
# 自动获取jwt_value
jwt_value = get_authorization_header(request)
if jwt_value:
try:
payload = jwt_decode_handler(jwt_value)
except jwt.ExpiredSignature:
raise AuthenticationFailed('签名过期!')
except jwt.DecodeError:
raise AuthenticationFailed("签名解码错误!")
except jwt.InvalidTokenError:
raise AuthenticationFailed('token无效!')
except Exception as e:
raise AuthenticationFailed(str(e))
user_obj = self.authenticate_credentials(payload)
print('user_obj.phone:', [user_obj.phone])
return user_obj, jwt_value # ['']
# 没有携带值,直接抛异常
raise AuthenticationFailed('没有携带认证信息')
views.py
from app02.utils import MyJwtAuthentication class Goods(APIView):
authentication_classes = [MyJwtAuthentication, ] def get(self, request, *args, **kwargs):
return Response('商品信息')
2. 自定义签发token
# 使用用户名,手机号,邮箱,都可以登录
# 前端需要传的数据格式
{
"username":"lq/13232333333/djd@163.com",
"password":'123'
}
1)多方式登录,逻辑写在序列化类中
views.py
from rest_framework.viewsets import ViewSet
from app02.ser import LoginModelSerializer # class Login1APIView(ViewSetMixin,APIView)
class Login1APIView(ViewSet): # 跟上面完全一样
"""
继承ViewSet意义:
1. 修改视图类中方法, 使用login明确提意
2. 继承了APIView, 具有较高的可控性
""" def login(self, request, *args, **kwargs):
# 1 需要 有个序列化的类
login_ser = LoginModelSerializer(data=request.data)
# 2 生成序列化类对象
# 3 调用序列化对象的is_validate
login_ser.is_valid(raise_exception=True)
token = login_ser.context.get('token')
username = login_ser.context.get('username')
return Response({'status': 1000, 'msg': '登录成功', 'token': token, 'username': username})
ser.py
from rest_framework import serializers
import re
from app01 import models
from rest_framework.exceptions import ValidationError
from rest_framework_jwt.utils import jwt_encode_handler, jwt_payload_handler class LoginModelSerializer(serializers.ModelSerializer):
# 需要覆盖低下的username字段,数据中是unique,post,认为你保存数据,自己校验不过,不然走不到validate
username = serializers.CharField()
class Meta:
model = models.UserInfo
fields = ['username', 'password'] def validate(self, attrs):
# 在这里写逻辑
username = attrs.get('username') # 用户名有三种方式
password = attrs.get('password')
# 通过判断,username数据不同,查询字段不一样
# 正则匹配,如果是手机号
if re.match(r'^1[3-9][0-9]{9}$', username):
user_obj = models.UserInfo.objects.filter(phone=username).first()
elif re.match('^.*?@.*?\.com$', username):
user_obj = models.UserInfo.objects.filter(email=username).first()
else:
user_obj = models.UserInfo.objects.filter(username=username).first()
if user_obj: # 用户存在
# 校验密码,因为是密文,要用check_password
if user_obj.check_password(password):
# 签发token
payload = jwt_payload_handler(user_obj)
token = jwt_encode_handler(payload)
self.context['token'] = token
self.context['username'] = user_obj.username
return attrs
else:
raise ValidationError('密码错误')
else:
raise ValidationError('用户不存在') '''
payload = jwt_payload_handler(user_obj) # 把user传入,得到payload
token = jwt_encode_handler(payload) # 把payload传入,得到token
'''
urls.py
urlpatterns = [
path('login1/', views.Login1APIView.as_view({'post': 'login'})),
]
2) 多方式登录,逻辑写在视图类中
views.py
# 视图代码
import re
from rest_framework.viewsets import ViewSet
from rest_framework_jwt.utils import jwt_payload_handler
from rest_framework_jwt.utils import jwt_encode_handler
from rest_framework.exceptions import ValidationError from app01.models import User class LoginAPIView(ViewSet):
"""
继承ViewSet意义:
1. 修改视图类中方法, 使用login明确提意
2. 继承了APIView, 具有较高的可控性
"""
def login(self, request, *args, **kwargs):
username = request.data.get('username')
password = request.data.get('password') # username=egon/111@qq.com/17621839222
if re.search(r'^1[3-9][0-9]{9}$', username):
user = User.objects.filter(phone=username).first()
elif re.search(r'^.*?@.*?qq\.com$', username):
user = User.objects.filter(email=username).first()
else:
user = User.objects.filter(username=username).first() if user:
# 校验密码,因为是密文,要用check_password
is_login = user.check_password(raw_password=password)
if is_login:
# 签发token
payload = jwt_payload_handler(user)
token = jwt_encode_handler(payload)
return Response({'status': 1000, 'token': token, 'results': {'username': user.username, 'email': user.email}})
raise ('用户密码错误!')
raise ValidationError("用户名错误!")
五. jwt的配置参数: 过期时间配置
import datetime JWT_AUTH = {
# 过期时间七天
'JWT_EXPIRATION_DELTA': datetime.timedelta(days=7),
}
六. base64编码与解码
# md5 固定长度,不可反解
# base63 变长,可反解 # 编码(字符串,json格式字符串)
import base64
import json dic = {'name': 'lq', 'age': 18, 'sex': '男'}
dic_str = json.dumps(dic) res = base64.b64encode(dic_str.encode('utf-8'))
print(res) # 解码
res1 = base64.b64decode(res)
res2 = json.loads(res1)
print(res1, res2)
drf(JWT认证)的更多相关文章
- DRF JWT认证(一)
为什么要使用JWT认证?构成和原理又是什么?怎么还有Base64的事?我都写了
- DRF JWT认证(二)
快速上手JWT签发token和认证,有这一篇就够了,DRF自带的和自定义的都帮你总结好了,拿去用~
- 9) drf JWT 认证 签发与校验token 多方式登陆 自定义认证规则反爬 admin密文显示
一 .认证方法比较 1.认证规则图 django 前后端不分离 csrf认证 drf 前后端分离 禁用csrf 2. 认证规则演变图 数据库session认证:低效 缓存认证:高效 jwt认证:高效 ...
- drf JWT认证模块与自定制
JWT模块 在djangorestframework中,有一款扩展模块可用于做JWT认证,使用如下命令进行安装: pip install djangorestframework-jwt 现在,就让我们 ...
- DRF JWT的用法 & Django的自定义认证类 & DRF 缓存
JWT 相关信息可参考: https://www.jianshu.com/p/576dbf44b2ae DRF JWT 的使用方法: 1. 安装 DRF JWT # pip install djang ...
- drf框架中jwt认证,以及自定义jwt认证
0909自我总结 drf框架中jwt 一.模块的安装 官方:http://getblimp.github.io/django-rest-framework-jwt/ 他是个第三方的开源项目 安装:pi ...
- drf组件之jwt认证
drf组件之jwt认证模块 一.认证规则 全称:json web token 解释:加密字符串的原始数据是json,后台产生,通过web传输给前台存储 格式:三段式 - 头.载荷.签名 - 头和载荷才 ...
- DRF框架(七) ——三大认证组件之频率组件、jwt认证
drf频率组件源码 1.APIView的dispatch方法的 self.initial(request,*args,**kwargs) 点进去 2.self.check_throttles(re ...
- drf框架 - JWT认证插件
JWT认证 JWT认证方式与其他认证方式对比: 优点 1) 服务器不要存储token,token交给每一个客户端自己存储,服务器压力小 2)服务器存储的是 签发和校验token 两段算法,签发认证的效 ...
- drf认证组件、权限组件、jwt认证、签发、jwt框架使用
目录 一.注册接口 urls.py views.py serializers.py 二.登录接口 三.用户中心接口(权限校验) urls.py views.py serializers.py 四.图书 ...
随机推荐
- Oracle删除索引规范
1.背景概述 2.索引删除规范 3.根本解决方案及建议 1.背景概述 近期应用升级上线过程中,存在删除业务表索引的变更操作,且因删除索引导致次日业务高峰时期,数据库响应缓慢的情况,经定位是缺失索引导致 ...
- c语言实现内存池
概要 所谓内存池,顾名思义和线程池的设计原理是一样的,为了减少频繁申请释放内存而带来的资源消耗,减少释放内存后产生的内存碎片. 设计理念 为了方便管理内存池的设计通常是划分出一定数量的内存块,这些内存 ...
- [Spring]Junit单元测试时取不到本地文件问题
Junit单元测试时取不到本地文件问题 问题 当使用Junit进行单元测试时,遇到了以下问题: 2022-10-24 16:13:19.902 [main] ERROR zonetop.gisboot ...
- Java官方文档
https://www.oracle.com/java/technologies/ https://www.oracle.com/java/technologies/downloads/archive ...
- Swoole从入门到入土(8)——协程初探
这一章节"协程"话题的讨论是为了让我们对之后协程风格服务端有更全面的了解.所以我们需要先一起了解一下什么是协程?协程有什么作用? 当大家第一次看到"协程"这个词 ...
- 在D2D环境下与GDI结合加载位图
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <Windows.h& ...
- xcode真机运行包添加(更新到15.4)
今天给真机装包,提示xcode版本低不能安装,电脑内存有些不足,xcode更新不了,这时候了解到xcode可以单独添加开发包\(^o^)/~ 1.下载 链接: https://pan.baidu.co ...
- FFmpeg开发笔记(十一):ffmpeg在移植到海思HI35xx平台之将ffmpeg库引入到sample的demo中
前言 上一篇交叉编译了ffmpeg的海思版本,使用交叉编译的qt的ffmpeg播放器在海思上播放,本片是将ffmpeg的环境添加进海思的sample环境中. 在海思sample中引入ffmpe ...
- Android switch语句报错Constant expression required
方案一 :可以用 if来替代 如下 原因:在Android Studio中使用JDK17以上版本,会出现switch语句报错"Constant expression required&qu ...
- Python列表转换成字典、嵌套列表转字典、多个列表转为字典嵌套列表
目录 两列表转为字典 多列表转为字典嵌套列表 嵌套列表转字典 方法一:直接内置dict 方法二: for循环 一个列表转字典 两列表转为字典 list1=["key1"," ...