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 四.图书 ...
随机推荐
- .NET 云原生架构师训练营(模块二 基础巩固 日志)--学习笔记
2.2.2 核心模块--日志 ILogger 的使用 日志的 ID 日志的分类 日志的级别 LoggerProvider 日志的最佳实践 .NET Core 和 ASP.NET Core 中的日志记录 ...
- Yum安装的Nginx安装新模块解决办法
Nginx版本1.22 Yum安装 Step1 去官网下载对应版本的源码包 Nginx源码包官网下载地址 Step2 上传到服务器 tar -xf nginx-1.22.1.tar.gz cd ngi ...
- JS Leetcode 220. 存在重复元素 III 题解分析,暴力解法与桶排序
壹 ❀ 引 今天的题目来自LeetCode 220. 存在重复元素 III,难度中等,题目描述如下: 给你一个整数数组 nums 和两个整数 k 和 t .请你判断是否存在 两个不同下标 i 和 j, ...
- NC213912 芭芭拉冲鸭~(续)
题目链接 题目 题目描述 芭芭拉这次来到了一棵字母树,这同样是一棵无根树,每个节点上面有一个小写字母. 芭芭拉想知道,自己从x冲刺到y,从x走到y收集所有字母,选择其中一部分字母组成一个回文串,这个回 ...
- NC50493 石子合并
题目链接 题目 题目描述 将n堆石子绕圆形操场排放,现要将石子有序地合并成一堆.规定每次只能选相邻的两堆合并成新的一堆,并将新的一堆的石子数记做该次合并的得分. 请编写一个程序,读入堆数n及每堆的石子 ...
- 解决ufw下pptp客户端连接问题
解决ufw下pptp客户端连接问题 解决ubuntu在启动ufw的情况下pptp客户端无法链接的问题. 修改/etc/ufw/before.rules 在COMMIT之前添加如下内容: -A ufw- ...
- SpringCloud服务注册与发现Eureka实战
介绍 Spring Cloud 封装了 Netflix 公司开发的 Eureka 模块来实现服务治理在传统的rpc远程调用框架中,管理每个服务与服务之间依赖关系比较复杂,管理比较复杂,所以需要使用服务 ...
- 《深入理解Java虚拟机》(二) GC 垃圾回收机制
@ 目录 一.概述 二.判断对象是否需要被回收方式 1.引用计数法: 2.可达性分析法: 三.垃圾收集算法 1.分代收集理论基础 2.标记-清除算法 3.复制-收集算法 4.标记-压缩(整理)算法 5 ...
- 推荐两个网络复用相关的 Go pkg: cmux smux
推荐两个网络复用相关的 Go pkg: cmux/smux 只写一下如何使用,不对实现进行大量描述,两个库的代码都比较精炼,花一会看一下就行. cmux 对端口进行复用,单端口可以建立不同协议的连接( ...
- 将JavaBean对象转换为Map集合
使用jackson-databind可以将JavaBean对象属性转换为Map集合. 添加配置依赖: <dependency> <groupId>com.fasterxml.j ...