jwt原理以及使用
Json web token (JWT), 是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准((RFC 7519).该token被设计为紧凑且安全的,特别适用于分布式站点的单点登录(SSO)场景。JWT的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源,也可以增加一些额外的其它业务逻辑所必须的声明信息,该token也可直接被用于认证,也可被加密。
原本通过客户端cookie和服务端session各存一个随机字符串的方式对服务端的存储压力很大,于是token应运而生,它只要求客户端存储一串加密字段,而不需要服务端存储。
jwt的组成
- 头header,用于声明类型,声明加密算法等,甚至会添加公司信息
- 荷载payload,存放有效信息的地方,过期时间、签发时间、用户id等
- 签名signature,是通过前两个部分通过秘钥+加密的方式得到的
整体是以.
隔开的三段密文,而密文的加密方式一般是base64。
示例:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ
base64编码
base64是可以解码的一种编码格式,它加密的结果一定是4的倍数(不足会以=补足)
import base64
import json
# base64编码
payload = {'user_id': 1, 'exp': 1234567890}
dic_str = json.dumps(payload) # 将字典转为字符串
enc_payload = base64.b64encode(dic_str.encode('utf-8')) # 二进制加密
print(enc_payload)
# base64解密
print(base64.b64decode('base64的密文')) # 是不是二进制都可以
base编码常用于网络运输,让数据看起来像密文,一般人就不去尝试解密了。
ps:因为base64是对二进制进行加密,所以照片数据等也都可以用base64加密传输
jwt签发认证流程
访问登录接口时,发送header和payload的数据,然后在服务端使用加密算法和秘钥进行签名,生成token,然后响应时将token返回给浏览器,让浏览器进行保存。
访问其他接口时,浏览器将token发往服务端,再次将header和payload按相同的加密方式和秘钥加密,得到的签名与token第三段(也就是第一次的签名)比对,比对成功则认证成功。
drf-jwt使用
django+drf 平台开发jwt有两个常用的模块:
- djangorestframework-jwt:老版本了,但一直可以用
- djangorestframework-simplejwt:实际工作中,公司用的多一些
- 也可以尝试自己封装jwt签发和认证
使用djangorestframework-jwt
设置登录接口
from rest_framework_jwt.views import obtain_jwt_token
urlpatterns = [
...
path('login/', obtain_jwt_token),
]
配置个路由即可,它会基于django原生的auth_user表去封装登录接口。
设置过期时间和返回格式
import datetime
JWT_AUTH = {
# 过期时间1天
'JWT_EXPIRATION_DELTA': datetime.timedelta(days=1),
# 如果不自定义,返回的格式是固定的,只有token字段
'JWT_RESPONSE_PAYLOAD_HANDLER': 'users.utils.jwt_response_payload_handler',
}
如果想要定制返回格式,让它不止有token字段,可以重写jwt_response_payload_handler,然后直接配置到字典中即可。
def jwt_response_payload_handler(token, user=None, request=None):
return {
'code': 100,
'msg': '登录成功',
'token': token,
'username': user.username
# 'icon':user.icon
}
# 配置一下
JWT_AUTH = {
'JWT_RESPONSE_PAYLOAD_HANDLER': 'app01.utils.jwt_response_payload_handler', }
认证类配置
rest_framework_jwt配套了认证类,可以直接导入视图类配置。
但是注意,这个认证类在没有传token时是直接通过的,这个逻辑有点小尴尬,所以一般配合一个drf的权限类使用。
from rest_framework_jwt.authentication import JSONWebTokenAuthentication
from rest_framework.permissions import IsAuthenticated
authentication_classes = [JSONWebTokenAuthentication]
permission_classes = [IsAuthenticated] # 登录用户有权限,不登录用户没权限
前端请求的请求头形式:Authorization=有效的token值
drf-jwt源码分析
首先,最基础的用法告诉我们,只需要配置个路由,就可以使用登录接口了。那么它所配置的obtain_jwt_token,我们就从这里开始分析。
obtain_jwt_token = ObtainJSONWebToken.as_view() # 是一个视图类
# 返回数据的格式是可以定制的
jwt_response_payload_handler = api_settings.JWT_RESPONSE_PAYLOAD_HANDLER
class ObtainJSONWebToken(JSONWebTokenAPIView):
# 视图类中继承了一个APIView,并配置了一个序列化类
serializer_class = JSONWebTokenSerializer
class JSONWebTokenAPIView(APIView):
permission_classes = ()
authentication_classes = ()
def 。。。
# 在父类中有一个函数是8大网络请求中的请求方式post
def post(self, request, *args, **kwargs):
# 可以配置的serializer
serializer = self.get_serializer(data=request.data)
if serializer.is_valid():
user = serializer.object.get('user') or request.user
token = serializer.object.get('token')
# 默认的response_data只拿一个{token:token}
response_data = jwt_response_payload_handler(token, user, request)
response = Response(response_data) # 产生响应对象
if api_settings.JWT_AUTH_COOKIE: # 如果设置了cookie的键
# 那么就发送cookie键值,我们可以将键设置为"token"
expiration = (datetime.utcnow() +
api_settings.JWT_EXPIRATION_DELTA)
response.set_cookie(api_settings.JWT_AUTH_COOKIE,
token,
expires=expiration,
httponly=True)
return response # 返回响应,可以保存到cookie
# 验证不成功时返回的响应
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
标签:JWT,base64,jwt,payload,token,使用,原理,response
From: https://www.cnblogs.com/Leethon-lizhilog/p/17106959.html