DRF
JWT原理介绍
Json web token(JWT),是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准(RFC 7519),该token被设计为紧凑且安全的,特别适用于分布式站点的单点登录(sso)场景,JWT的声明一般被用来在身份提供者和服务者间传递被认证的用户身份信息,以便于从资源服务器获取资源,也可以增加一些额外的其他业务逻辑所必须的声明信息,该token也可直接被用于认证,也可被加密token的应用于web方向的称之为jwt。
JWT的构成
JWT就是一段字符串,由三段信息构成的,将这三段信息文本用.链接一起就构成了Jwt字符串。就像这样:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ
第一部分我们称它为头部(header),第二部分我们称其为载荷(payload, 类似于飞机上承载的物品),第三部分是签证(signature)。
header:头
jwt的头部承载两部分信息: 声明类型,这里是jwt 声明加密的算法 通常直接使用 HMAC SHA256 完整的头部就像下面这样的JSON:
{ 'typ': 'JWT', 'alg': 'HS256' }
然后将头部进行base64加密(该加密是可以对称解密的),构成了第一部分。变成了(base64的编码)`eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9`
payload:荷载
荷载就是存放有效信息的地方。这个名字像是特指飞机上承载的货品,这些有效信息包含三个部分:
- 标准中注册的声明
- 公共的声明
- 私有的声明
signature:签名
JWT的第三部分是一个签证信息,这个签证信息由三部分组成:
header (base64后的) - payload (base64后的) - secret 这个部分需要base64加密后的header和base64加密后的payload使用.连接组成的字符串。
然后通过header中声明的加密方式进行加盐secret组合加密,然后就构成了jwt的第三部分。 将这三部分用.连接成一个完整的字符串,构成了最终的jwt:
`eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ `
base64的编码与解码
base64 可以把字符串编码成base64的编码格式:(大小写字母,数字和 =)
eyJzdWIiOiAiMTIzNDU2Nzg5MCIsICJuYW1lIjogImxxeiIsICJhZG1pbiI6IHRydWV9
可以把base64编码的字符串,解码回原来的格式
应用场景: - jwt中使用 - 网络中传输字符串就可以使用base64编码 - 网络中传输图片,也可能使用base64的编码
base64编码和解码的过程
编码
import json import base64 d = {'name': 'zxr', 'userid': 1, 'age': 18} info = json.dumps(d) # print(info) # {"name": "zxr", "userid": 1, "age": 18} # 把字符串转成bytes格式的俩种方式 # 第一种:info.encode('utf-8') # res = base64.b64encode(info.encode('utf-8')) # print(res) # b'eyJuYW1lIjogInp4ciIsICJ1c2VyaWQiOiAxLCAiYWdlIjogMTh9' # 第二种:强转 print(bytes(info, encoding='utf-8')) # b'{"name": "zxr", "userid": 1, "age": 18}'
解码
import json import base64 d = {'name': 'zxr', 'userid': 1, 'age': 18} info = json.dumps(d) b'eyJuYW1lIjogInp4ciIsICJ1c2VyaWQiOiAxLCAiYWdlIjogMTh9' res = base64.b64decode('eyJuYW1lIjogInp4ciIsICJ1c2VyaWQiOiAxLCAiYWdlIjogMTh9') print(res) # b'{"name": "zxr", "userid": 1, "age": 18}'
drf-JWT快速使用
django中使用jwt:
# 可以自己写 https://github.com/jpadilla/django-rest-framework-jwt (比较老) https://github.com/jazzband/djangorestframework-simplejwt (比较新)
安装:
pip3 install djangorestframework-jwt
快速使用:
- 迁移表,因为它默认使用auth的user表签发token
- 创建超级用户(auth的user表中要右记录)
- 不需要写登录接口了,如果是使用auth的user表作为用户表,它可以快速签发
- 签发(登录):只需要在路由中配置(因为它帮咱们写好登录接口了)
from rest_framework_jwt.views import obtain_jwt_token urlpatterns = [ path('login/', obtain_jwt_token), ] # 返回结果 { "token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoxLCJ1c2VybmFtZSI6Inp4ciIsImV4cCI6MTY2NTU3MjU4OSwiZW1haWwiOiJ6eHJAMTYzLmNvbSJ9.0jDd56Jk04-SdZ4AchLHkcfLECS1RvhFwAQ8VoNKiMM" }
- 认证(认证类):导入,配置在视图类上
from rest_framework.permissions import IsAuthenticated from rest_framework.response import Response from rest_framework.views import APIView from rest_framework_jwt.authentication import JSONWebTokenAuthentication class TestView(APIView): authentication_classes = [JSONWebTokenAuthentication, ] # 是否登录的权限类 permission_classes = [IsAuthenticated, ] def get(self, request): return Response('ok')
- 前端访问时,token需要放在请求头中
Authorization:jwt token串 # 例如: Authorization:jwt eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoxLCJ1c2VybmFtZSI6Inp4ciIsImV4cCI6MTY2NTU3MzMzMCwiZW1haWwiOiJ6eHJAMTYzLmNvbSJ9.DAX1wLhHYnpGwVzGvMnSFzUudkPgXsYvlrfk-XFdSAc
drf-jwt修改返回格式
登录成功后,前端看到的格式,太固定了,只有token,我们想做成:
{code:100,msg:'登录成功',token:adfasdfasdf}
固定写法:
写一个函数,函数返回什么,前端就看到什么,配置在配置文件中
使用步骤:
写一个函数:
def jwt_response_payload_handler(token, user=None, request=None): return { 'code': 100, 'msg': '登录成功', 'username': user.username, 'token': token }
将函数配置在配置文件中:
# djangorestframework-jwt的配置,这个配置了以后优先使用这个 JWT_AUTH = { 'JWT_RESPONSE_PAYLOAD_HANDLER': 'app01.response.jwt_response_payload_handler', }
自定义user表,签发token:
# view.py from rest_framework.permissions import IsAuthenticated from rest_framework.response import Response from rest_framework.views import APIView from rest_framework_jwt.authentication import JSONWebTokenAuthentication from app01.models import Userinfo from rest_framework.exceptions import APIException from rest_framework_jwt.settings import api_settings jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER # 自己基于自定义的用户表签发token class UserView(APIView): def post(self, request): try: username = request.data.get('username') password = request.data.get('password') user = Userinfo.objects.get(username=username, password=password) payload = jwt_payload_handler(user) token = jwt_encode_handler(payload) return Response({'code': 100, 'msg': '登录成功', 'token': token}) except Exception as e: raise APIException('用户表名或密码错误')
# url.py from django.contrib import admin from django.urls import path from rest_framework_jwt.views import obtain_jwt_token from app01 import views urlpatterns = [ path('user/login/', views.UserView.as_view()), ]
# models.py from django.db import models # Create your models here. class Userinfo(models.Model): username = models.CharField(max_length=32) password = models.CharField(max_length=32)
自定义认证类,验证token:
# view.py class UserTestView(APIView): authentication_classes = [JWTAuthentication, ] def get(self, request): return Response('ok')
# 自定义认证类 from rest_framework import exceptions from rest_framework.authentication import BaseAuthentication from rest_framework_jwt.settings import api_settings from rest_framework.exceptions import AuthenticationFailed import jwt from .models import Userinfo jwt_decode_handler = api_settings.JWT_DECODE_HANDLER class JWTAuthentication(BaseAuthentication): def authenticate(self, request): token = request.META.get('HTTP_TOKEN') print(token) jwt_value = request.META.get('HTTP\_TOKEN') if jwt_value: 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() user = payload.get('user_id') return user, jwt_value else: raise AuthenticationFailed('您没有携带token')
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步