jwt原理

使用jwt认证和使用session认证的区别

三段式

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ

# 1. header jwt的头部承载两部分信息:
声明类型 这里是jwt
声明加密的算法 通常直接使用 HMSC SHA256
公司信息
{
'typ':'jwt',
'alg':'HS256'
}
然后将头部进行base64编码 构成了第一部分
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9 # 2. payload 载荷就是存放有效信息的地方
当前用户信息 用户名 用户id
exp:jwt的过期时间 这个过期时间必须要大于签发时间 定义一个payload:
{
"exp":"1234567890",
"name":"jason",
"user_id":99
}
然后将其进行base64编码 得到jwt的第二部分
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9 # signature JWT的第三部分是一个签证信息,这个签证信息由三部分组成: header (base64后的)
payload (base64后的)
secret
这个部分需要base64加密后的header和base64加密后的payload使用.连接组成的字符串,然后通过header中声明的加密方式进行加盐secret组合加密,然后就构成了jwt的第三部分。 将这三部分用.连接成一个完整的字符串,构成了最终的jwt: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ

jwt开发流程

# 使用jwt认证的开发流程 就是两部分
第一部分:签发token的过程 登录做的
用户携带用户名和密码 访问我 我们校验通过,生成token串,返回给前端
第二部分:token认证过程 登录认证时使用 其实就是咱们之前讲的认证类 在认证类中完成对token的认证操作
用户访问我们需要登陆后才能访问的接口,必须携带我们签发的token串(请求头)
我们取出token 验证该token是否过期,是否被篡改,是否是伪造的
如果正常,说明荷载中的数据,就是安全的,可以根据荷载中的用户id,查询出当前登录用户,放到request中即可

drf-jwt快速使用

# djagno+drf框架中,使用jwt来做登录认证

# 使用第三方:
-django-rest-framework-jwt:https://github.com/jpadilla/django-rest-framework-jwt
-djangorestframework-simplejwt:https://github.com/jazzband/djangorestframework-simplejwt # 我们可以自己封装 :https://gitee.com/liuqingzheng/rbac_manager/tree/master/libs/lqz_jwt # 安装
pip3 install djangorestframework-jwt # 补充:
密码明文一样,每次加密后都不一样,如何做,动态加盐,动态的盐要保存,跟密码保存在一起
有代码 有数据库 没有登不进去的系统 # 解决不了:
token被获取,模拟发送请求
不能篡改
不能伪造 # 快速使用
签发过程(快速签发),必须是auth的user表(人家帮你写好了)
登录接口--》基于auth的user表签发的
认证过程
登录认证,认证类(写好了) # 总结:
签发:只需要在路由中配置
from rest_framework_jwt.views import obtain_jwt_token
urlpatterns = [
path('login/', obtain_jwt_token),
]
认证:视图类上加
class BookView(ViewSet):
authentication_classes = [JSONWebTokenAuthentication] # 认证类,drf-jwt提供的
permission_classer = [IsAuthenticated] # 权限类 drf提供的
访问的时候 要在请求头中携带 必须叫
Authorization:jwt token串

drf-jwt定制返回格式

# 登录签发token的接口 要返回code,msg,username,token等信息
参照:jwt源码
from rest_framework_jwt import settings # 配置文件
from rest_framework_jwt.utils import jwt_response_payload_handler # 返回格式 # 对返回格式进行定制
1 写个函数 函数返回字典格式 返回的格式 会被序列化 前端看到
def common_response(token, user=None, request=None):
return {
'code':100,
'msg':'登录成功',
'token':token,
'username':user.username
}
2 写的函数在配置文件中配置
JWT_AUTH = {
'JWT_RESPONSE_PAYLOAD_HANDLER':'app01.jwt_response.common_response',
}

drf-jwt自定义用户表签发

# 1 创建一个用户表
class User(models.Model):
username = models.CharField(max_length=32)
password = models.CharField(max_length=64)
age = models.IntegerField()
email = models.CharField(max_length=64)
# 2 登录接口
jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER
class UserView(ViewSet):
def login(self,request):
username = request.data.get('username')
password = request.data.get('password')
user = User.objects.filter(username=username,password=possword).first()
if user:
# 登录成功,签发token-->先背过,
# 1 通过user 获取payload
payload = jwt_payload_handler(user)
print(payload)
# 2 通过payload 得到token
token = jwt_encode_handler(payload)
return Response({'code': 100, 'msg': '登录成功', 'username': user.username, 'token': token})
else:
return Response({'code': 101, 'msg': '用户名或密码错误'})
# 3 登录接口签发token返回给前端

drf-jwt自定义认证类

# drf的认证类定义方式,之前学过
# 在认证类中,自己写逻辑
from rest_framework.authentication import BaseAuthentication
from rest_framework.exceptions import AuthenticationFailed
import jwt
from rest_framework_jwt.settings import api_settings
from .models import User
jwt_decode_handler = api_settings.JWT_DECODE_HANDLER # 根据JSONWebTokenAuthentication重写authenticate方法
from rest_framework_jwt.authentication import JSONWebTokenAuthentication class JWTAuthentication(BaseAuthentication):
def authenticate(self,request):
token = request.META.get('HTTP_TOKEN')
# 背过
# 校验token是否过期 合法 如果都通过 查询出当前用户 返回
# 如果不通过 抛异常
try:
payload = jwt_decode_handler(token)
# 如果认证通过 payload就可以认为是安全的 我们就可以使用
user_id = payload.get('user_id')
# 每个需要登录后 才能访问的接口 都会走这个认证类 一旦走到这个认证类,就会去数据库查询一次数据
user = User.objects.get(pk=user_id)
# 优化后的
# user = User(username=payload.get('username'),id=user_id)
# user = {'username':payload.get('username'),'id':user_id}
except jwt.ExpiredSignature:
raise AuthenticationFailed('token过期')
except jwt.DecodeError:
raise AuthenticationFailed('解码失败')
except jwt.InvalidTokenError:
raise AuthenticationFailed('token认证异常')
except Exception:
raise AuthenticationFailed('token认证异常')
return user,token # 局部使用和全局使用

drf-jwt的签发源码分析

# from rest_framework_jwt.views import obtain_jwt_token
# obtain_jwt_token就是ObtainJSONWebToken.as_view()---》视图类.as_view() # 视图类
class ObtainJSONWebToken(JSONWebTokenAPIView):
serializer_class = JSONWebTokenSerializer # 父类:JSONWebTokenAPIView
class JSONWebTokenAPIView(APIView):
# 局部禁用掉权限和认证
permission_classes = ()
authentication_classes = ()
def post(self, request, *args, **kwargs):
# serializer=JSONWebTokenSerializer(data=request.data)
serializer = self.get_serializer(data=request.data)
# 调用序列化列的is_valid---》字段自己的校验规则,局部钩子和全局钩子
# 读JSONWebTokenSerializer的局部或全局钩子
if serializer.is_valid(): # 全局钩子在校验用户,生成token
# 从序列化类中取出user
user = serializer.object.get('user') or request.user
# 从序列化类中取出token
token = serializer.object.get('token')
# 咱么定制返回格式的时候,写的就是这个函数
response_data = jwt_response_payload_handler(token, user, request)
response = Response(response_data)
return response
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) # JSONWebTokenSerializer的全局钩子
class JSONWebTokenSerializer(Serializer):
def validate(self, attrs):
# attrs前端传入的,校验过后的数据 {username:lqz,password:lqz12345}
credentials = {
'username':attrs.get('username'),
'password': attrs.get('password')
}
if all(credentials.values()): # 校验credentials中字典的value值是否都不为空
# user=authenticate(username=前端传入的,password=前端传入的)
# auth模块的用户名密码认证函数,可以传入用户名密码,去auth的user表中校验用户是否存在
# 等同于:User.object.filter(username=username,password=加密后的密码).first()
user = authenticate(**credentials)
if user:
if not user.is_active:
msg = _('User account is disabled.')
raise serializers.ValidationError(msg)
payload = jwt_payload_handler(user)
return {
'token': jwt_encode_handler(payload),
'user': user
}
else:
msg = _('Unable to log in with provided credentials.')
raise serializers.ValidationError(msg)
else:
msg = _('Must include "{username_field}" and "password".')
msg = msg.format(username_field=self.username_field)
raise serializers.ValidationError(msg)

drf-jwt的认证源码分析

# from rest_framework_jwt.authentication import JSONWebTokenAuthentication
# JSONWebTokenAuthentication
class JSONWebTokenAuthentication(BaseJSONWebTokenAuthentication):
def get_jwt_value(self, request):
# request-->jwt 'token串'
auth = get_authorization_header(request).split()
# 按照空格切割之后-->auth=['jwt','token串']
auth_header_prefix = api_settings.JWT_AUTH_HEADER_PREFIX.lower()
if not auth:
if api_settings.JWT_AUTH_COOKIE:
return request.COOKIES.get(api_settings.JWT_AUTH_COOKIE)
return None
if smart_text(auth[0].lower()) != auth_header_prefix:
return None
if len(auth) == 1:
msg = _('Invalid Authorization header. No credentials provided.')
raise exceptions.AuthenticationFailed(msg)
elif len(auth) > 2:
msg = _('Invalid Authorization header. Credentials string '
'should not contain spaces.')
raise exceptions.AuthenticationFailed(msg)
return auth[1] # auth取索引1 ---> token串 # 因为我们重写了authenticate
# JSONWebTokenAuthentication中没有要从父类中查找
# 父类中找:BaseJSONWebTokenAuthentication---》authenticate,找到了
def authenticate(self, request):
# 调JSONWebTokenAuthentication中的get_jwt_value
# 拿到前端传入的token串
jwt_value = self.get_jwt_value(request)
# 如果前端没传 返回None,request.user中就没有当前登录用户
# 如果前端没有携带token,也能进入到视图类的方法中执行,控制不住登录用户
# 所以咱们才加了个权限类,来做控制
if jwt_value is None:
return None
try:
payload = jwt_decode_handler(jwt_value)
except jwt.ExpiredSignature:
msg = _('Signature has expired.')
raise exceptions.AuthenticationFailed(msg)
except jwt.DecodeError:
msg = _('Error decoding signature.')
raise exceptions.AuthenticationFailed(msg)
except jwt.InvalidTokenError:
raise exceptions.AuthenticationFailed()
# 调用authenticate_credentials通过payload会返回当前登录用户
user = self.authenticate_credentials(payload)
return (user, jwt_value) # 如果用户不携带token,也能认证通过
# 所以我们必须加个权限类来限制
from rest_framework.permissions import IsAuthenticated # 是否认证通过
class IsAuthenticated(BasePermission):
def has_permission(self, request, view):
# 必须有当前登录用户且已经完成认证
return bool(request.user and request.user.is_authenticated)

drf——jwt的更多相关文章

  1. DRF JWT的用法 & Django的自定义认证类 & DRF 缓存

    JWT 相关信息可参考: https://www.jianshu.com/p/576dbf44b2ae DRF JWT 的使用方法: 1. 安装 DRF JWT # pip install djang ...

  2. DRF JWT认证(一)

    为什么要使用JWT认证?构成和原理又是什么?怎么还有Base64的事?我都写了

  3. DRF JWT认证(二)

    快速上手JWT签发token和认证,有这一篇就够了,DRF自带的和自定义的都帮你总结好了,拿去用~

  4. django drf JWT

    建议使用djangorestframework-jwt或者djangorestframework_simplejwt,文档为 https://github.com/GetBlimp/django-re ...

  5. 9) drf JWT 认证 签发与校验token 多方式登陆 自定义认证规则反爬 admin密文显示

    一 .认证方法比较 1.认证规则图 django 前后端不分离 csrf认证 drf 前后端分离 禁用csrf 2. 认证规则演变图 数据库session认证:低效 缓存认证:高效 jwt认证:高效 ...

  6. drf JWT认证模块与自定制

    JWT模块 在djangorestframework中,有一款扩展模块可用于做JWT认证,使用如下命令进行安装: pip install djangorestframework-jwt 现在,就让我们 ...

  7. drf☞jwt自动签发与手动签发

    目录 一.自动签发 二.手动签发 一.自动签发 urls from rest_framework_jwt.views import obtain_jwt_token # 使用jwt自带的登录视图 ur ...

  8. django drf框架中的user验证以及JWT拓展的介绍

    登录注册是几乎所有网站都需要去做的接口,而说到登录,自然也就涉及到验证以及用户登录状态保存,最近用DRF在做的一个关于网上商城的项目中,引入了一个拓展DRF JWT,专门用于做验证和用户状态保存.这个 ...

  9. drf框架 - JWT认证插件

    JWT认证 JWT认证方式与其他认证方式对比: 优点 1) 服务器不要存储token,token交给每一个客户端自己存储,服务器压力小 2)服务器存储的是 签发和校验token 两段算法,签发认证的效 ...

  10. DRF之三大认证

    一.用户认证Authorticatons 1.源码解析 第一步. 找入口 def dispatch(self, request, *args, **kwargs): # 1.首先我们进入的是APIVi ...

随机推荐

  1. 四种OS网络属性配置

    1.Windows BAT设置网络属性: netsh interface ip set address "Ethernet" static 192.168.31.223 255.2 ...

  2. java方法参数(超详细)

    前言 在上一篇文章中,壹哥给大家讲解了方法的定义.调用和返回值,但方法的内容还有很多,比如方法的参数是怎么回事?接下来壹哥会在这篇文章中,继续给大家讲解方法参数相关的知识,这就是我们今天要学习的内容. ...

  3. Shell脚本监控Centos 7系统运行状态

    #!/usr/bin/bash ## @date: 2021-08-17 ## This is a script for security operation indicator monitoring ...

  4. CSharp linq 的本质

    linq 的本质是扩展方法和委托的结合 链式 linq 和 方法级linq List<int> list = new List<int>() { 3,9,32,7 }; // ...

  5. 【深入浅出 Yarn 架构与实现】5-3 Yarn 调度器资源抢占模型

    本篇将对 Yarn 调度器中的资源抢占方式进行探究.分析当集群资源不足时,占用量资源少的队列,是如何从其他队列中抢夺资源的.我们将深入源码,一步步分析抢夺资源的具体逻辑. 一.简介 在资源调度器中,以 ...

  6. react中自定义函数、生命周期钩子函数、修改状态、组件、组件传值

    1.回顾 2.自定义函数 事件的首字母大小 onclick ==> onClick onchange ==> onChange 普通的点击事件 ---- 调用事件不加(),加了立即执行 i ...

  7. 搭建react的架手架

    1.回顾 cnpm i @vue/cli -g ----- 4的脚手架 ------ webpack 4 cnpm i @vue/cli@3 -g ----- 3的脚手架 ------ webpack ...

  8. 微软博客上几篇 Semantic-kernel (SK)文章

    自从最近微软开源Semantic-kernel  来帮助开发人员在其应用程序中使用AI大型语言模型(LLM)以来,Microsoft一直在忙于改进它,发布了有关如何使用它的新指南并发布了5篇文章介绍他 ...

  9. CSS伪元素详解以及伪元素与伪类的区别

    前面已经介绍过CSS伪类的知识,具体可见前文 CSS伪类知识详解. 伪元素常常被误解为伪类,主要在于他们的语法相似,都是对于选择器功能的扩展,相似程度很高导致被混淆. 本文通过详细介绍伪元素和常见的使 ...

  10. arc076f F - Exhausted?

    ARC076 F - Exhausted? [题目大意] \(有m个座位,分别位于坐标为1,2,3,...,m的地方:n个客人,第i位客人只坐位于[0,li]∪[ri,m]的座位.每个座位只能坐一个人 ...