- JWT介绍
- JWT的组成部分
- payload
- signature
- jwt认证算法:签发与校验
- 1.
JWT
分成3部分:头
(header)、体
(payload)、签名
(signature) - 2.
头
(header)和体
(payload)是可逆
加密,让服务器可以反解出user对象;签名
(signature)是不可逆
加密,保证整个token的安全性的 - 3.头体签名三部分,都是采用
json格式
的字符串,进行加密,可逆加密一般采用base64算法
,不可逆加密一般采用hash(md5)
算法 - 4.
头
(header)中的内容是基本信息:公司信息、项目组信息、token采用的加密方式信息 - 5.
体
(payload)中的内容是关键信息
:用户主键、用户名、签发时客户端信息(设备号、地址)、过期时间 - 6.签名中的内容时安全信息:
头的加密结果
+体的加密结果
+ 服务器不对外公开的安全码
进行md5加密
- 1.
- 签发
- 校验
- drf项目的jwt认证开发流程(重点)
- JWT的安装与使用
- 自定义基于jwt的认证类
JWT介绍
简介
JSON Web Token (JWT)是一个开放标准(RFC 7519),它定义了一种紧凑的、自包含的方式,用于作为JSON对象在各方之间安全地传输信息。该信息可以被验证和信任,因为它是数字签名的。
构成
JWT
的本质就是token
,它主要有三
部分组成,分别是头部(header)
、荷载(payload)
主题部、以及签证(signature)
。
前两部分都是由base64
进行编码(可以反解码),后一部分是不可反解的加密,由前两部分base64
的结果加密(hash256)后组成。各个部分之间由.
来分割
JWT的组成部分
header
在header
中,一般携带着2部分信息
(声明):
- 声明类型:这里用的是
JWT
- 声明加密的算法:通常直接使用
HMAC
SHA256
除此之外,也可以添加其他声明:比如说,添加公司名称等信息
自定义header
,JSON
格式:
{
"alg": "HS256",
"typ": "JWT"
}
用base64
对其进行编码,得到JWT
中的header
部分:
ewogICAgInR5cGUiOiJKV1QiLAogICAgImVuY29kZV9tZXRob2QiOiJIQVNIMjU2Igp9
payload
荷载部作为JWT
三部分中的第二部分,都是存放有效信息
。
它可以存放三
种类型的有效信息:
- 标准中注册的声明
- 公共的声明
- 私有的声明
标准中注册的声明(建议但不强制使用):
荷载部位的key | 描述 |
---|---|
iss | JWT签发者(服务端) |
sub | JWT所面向的用户 |
aud | 接收JWT的一方 |
exp | JWT的过期时间,该时间必须大于签发时间 |
nbf | 再某一时间段之前,该JWT不可用 |
jti | JWT的唯一身份标识,主要用作一次性token,回避时序攻击 |
公共的声明:
公共的声明可以添加任何的信息,一般添加用户的相关信息或其他业务需要的必要信息,但不建议添加敏感信息
,因为该部分在客户端可解密
。
私有的声明:
私有声明是提供者和消费者所共同定义的声明,一般不建议存放敏感信息,因为base64
是对称解密的,意味着该部分信息可以归类为明文信息
。
自定义payload
,JSON
格式:
{
"sub": "1234567890",
"name": "John Doe",
"iat": 1516239022
}
用base64
对其进行编码,得到JWT
中的payload
部分:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
signature
JWT
的第三部分是一个签证
信息,这个签证信息由三
部分组成
- header (base64后的)
- payload (base64后的)
- secret
这个部分需要base64
加密后的header
和base64
加密后的payload
使用.
连接组成的字符串,然后通过header
中声明的加密方式进行加盐secret
组合加密,然后就构成了JWT
的第三部分。
signature = hashlib.sha256()
signature.update(header_payload_result)
salt = 'llnb'
signature.update(salt.encode("utf-8")) # 加盐
signature_result = signature.hexdigest() # 获得结果
JWT
第三部分结果
b465493b91a918d9d957f61f8222f0f6dcbb58d428977543be33b1ee5067f2
jwt认证算法:签发与校验
1.JWT
分成3部分:头
(header)、体
(payload)、签名
(signature)
2.头
(header)和体
(payload)是可逆
加密,让服务器可以反解出user对象;签名
(signature)是不可逆
加密,保证整个token的安全性的
3.头体签名三部分,都是采用json格式
的字符串,进行加密,可逆加密一般采用base64算法
,不可逆加密一般采用hash(md5)
算法
4.头
(header)中的内容是基本信息:公司信息、项目组信息、token采用的加密方式信息
{
"company": "公司信息",
"type": "JWT",
"encode_method": "HASH256"
...
}
5.体
(payload)中的内容是关键信息
:用户主键、用户名、签发时客户端信息(设备号、地址)、过期时间
{
"id": 1024,
"sub": "1233211234567",
"name": "kevin",
"admin": true
}
6.签名中的内容时安全信息:头的加密结果
+ 体的加密结果
+ 服务器不对外公开的安全码
进行md5加密
{
"head": "头的加密字符串",
"payload": "体的加密字符串",
"secret_key": "安全码"
}
签发
根据登录请求提交来的 账号 + 密码 + 设备信息 签发 token
- 用基本信息存储json字典,采用base64算法加密得到
头字符串
- 用关键信息存储json字典,采用base64算法加密得到
体字符串
- 用头、体加密字符串再加安全码信息存储json字典,采用hash md5算法加密得到
签名字符串
账号密码就能根据User表得到user对象,形成的三段字符串 用 .
拼接成token返回给前台
校验
根据客户端带token的请求,反解出 user 对象
- 将token按
.
拆分为三段字符串,第一段 头加密字符串 一般不需要做任何处理 - 第二段 体加密字符串,要反解出
用户主键
,通过主键从User表中就能得到登录用户,过期时间和设备信息都是安全信息,确保token没过期,且是同一设备来的 - 再用
第一段 + 第二段 + 服务器安全码
不可逆md5加密,与第三段签名字符串
进行碰撞校验,通过后才能代表第二段校验得到的user对象就是合法的登录用户
drf项目的jwt认证开发流程(重点)
- 用账号密码访问登录接口,登录接口逻辑中调用
签发token 算法
,得到token
,返回给客户端
,客户端自己存到cookies
中 校验token的算法
应该写在认证类中(在认证类中调用),全局
配置给认证组件,所有视图类请求
,都会进行认证校验
,所以请求带了token
,就会反解出user
对象,在视图类中用request.user
就能访问登录的用户
JWT的安装与使用
安装
pip install djangorestframework-jwt
使用
快速使用(默认使用auth的user表)
1.在默认auth
的user
表中创建一个用户
2.在路由中配置
urlpatterns = [
path('login/', obtain_jwt_token),
]
3.用postman
向这个地址发送post请求
,携带用户名、密码
,登陆成功就会返回token
4.obtain_jwt_token
本质也是一个视图类
,继承了APIView
- 通过前端传入的用户名密码,校验用户,如果校验通过,生成token,返回
- 如果校验失败,返回错误信息
5.如果用户携带了token,并且配置了JSONWebTokenAuthentication
,从request.user
就能拿到当前登录用户,如果没有携带,当前登录用户就是匿名用户
6.前端要发送请求,携带jwt,格式必须如下
- 把token放到请求头中,key为:Authorization
- value必须为:jwt
用户登录以后才能访问某个接口
jwt模块内置了认证类,拿过来局部配置就可以
class OrderView(APIView):
# 只配它不行,不管是否登录,都能范围,需要搭配一个内置权限类
authentication_classes = [JSONWebTokenAuthentication, ]
permission_classes = [IsAuthenticated,]
def get(self, request):
print(request.user.username)
return Response('ok')
自定义基于jwt的认证类
自定义用户表签发token
使用
rom rest_framework_jwt.settings import api_settings
jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER
from rest_framework.decorators import action
class UserView(ViewSet):
@action(methods=['POST'],detail=False)
def login(self, request):
username = request.data.get('username')
password = request.data.get('password')
user = UserInfo.objects.filter(username=username, password=password).first()
if user:
# 登录成功---》签发token
payload = jwt_payload_handler(user) # 根据当前登录用户获取荷载
print(payload)
token = jwt_encode_handler(payload) # 根据荷载生成token
return Response({'code': 100, 'msg': '登录成功', 'token': token})
else:
return Response({'code': 101, 'msg': '用户名或密码错误'})
自定义认证类验证token
使用
from rest_framework.authentication import BaseAuthentication
from rest_framework_jwt.settings import api_settings
from .models import UserInfo
from rest_framework import exceptions
jwt_decode_handler = api_settings.JWT_DECODE_HANDLER
jwt_get_username_from_payload = api_settings.JWT_PAYLOAD_GET_USERNAME_HANDLER
class LoginAuth(BaseAuthentication):
def authenticate(self, request):
token = request.META.get('HTTP_TOKEN')
if not token:
raise exceptions.APIException('请先登录')
# 验证token是否合法
# try:
# payload = jwt_decode_handler(token)
# except jwt.ExpiredSignature:
# msg = 'token过期了'
# raise exceptions.AuthenticationFailed(msg)
# except jwt.DecodeError:
# msg = 'token解码错误'
# raise exceptions.AuthenticationFailed(msg)
# except jwt.InvalidTokenError:
# msg = '解析token未知错误'
# raise exceptions.AuthenticationFailed(msg)
try:
payload = jwt_decode_handler(token)
except Exception:
raise exceptions.APIException('token错误')
user = UserInfo.objects.filter(pk=payload['user_id']).first()
return user, token
全局使用
setting.py
REST_FRAMEWORK = {
authentication_classes = [JwtAuthentication, ],
}
局部使用
views.py
class Mine(APIView):
authentication_classes = [JwtAuthentication, ]
permission_classes = [IsAuthenticated,]
...
局部禁用
class Mine(APIView):
authentication_classes = []
控制登录接口返回的数据格式
utils
from homework.serializer import UserReadOnlyModelSerializer
def jwt_response_payload_handler(token, user=None, request=None):
return {
'code': 200,
'username': user.username,
'msg': '登录成功',
'token': token
}
settings.py
# 配置文件配置
JWT_AUTH = {
'JWT_RESPONSE_PAYLOAD_HANDLER': 'app01.utils.jwt_response_payload_handler',
}