drf——jwt
2023/5/30 5:22:16
本文主要是介绍drf——jwt,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
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的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-04-2203-为啥大模型LLM还没能完全替代你?
- 2024-04-2101-大语言模型发展
- 2024-04-17基于SpringWeb MultipartFile文件上传、下载功能
- 2024-04-14个人开发者,Spring Boot 项目如何部署
- 2024-04-14RAG应用开发实战02-相似性检索的关键 - Embedding
- 2024-04-14出海软件草根逆袭打法是什么?
- 2024-04-13鸿蒙原生应用再新丁!企查查 碧蓝航线 入局鸿蒙
- 2024-04-11RAG应用开发实战(01)-RAG应用框架和解析器
- 2024-04-10DevOps已死?2024年的DevOps将如何发展
- 2024-04-10码农必看:常见源代码混淆技术详解