jwt原理解析、jwt开发流程、jwt介绍和快速使用、定制返回格式、自定义用户表,签发
文章目录
一、jwt原理解析
在用户注册或登录后,我们想记录用户的登录状态,或者为用户创建身份认证的凭证。我们不再使用Session认证机制,而使用Json Web Token(本质就是token)认证机制。
Json web token (JWT), 是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准((RFC 7519).该token被设计为紧凑且安全的,特别适用于分布式站点的单点登录(SSO)场景。JWT的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源,也可以增加一些额外的其它业务逻辑所必须的声明信息,该token也可直接被用于认证,也可被加密。
基于session的认证
基于jwt的认证
1.1、jwt的构成和工作原理
1.1.1、JWT的构成
JWT就是一段字符串,由三段信息构成的,将这三段信息文本用.链接一起就构成了Jwt字符串。就像这样:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ
第一部分我们称它为头部(header),第二部分我们称其为载荷(payload, 类似于飞机上承载的物品),第三部分是签证(signature).
1.1.2、header (头部)
jwt的头部承载两部分信息:
- 声明类型:这里是jwt
- 声明加密的算法:通常直接使用 HMAC SHA256
完整的头部信息就像下面这样的JSON:
'''
{
'type': 'JWT',
'alg': 'HS256'
}
'''
然后将头部进行base64编码,构成了第一部分。
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9
1.1.2、payload(载荷)
载荷就是存放有效信息的地方。这个名字像是特指飞机上承载的货品,这些有效信息包含三个部分
- 标准中注册的声明
- 公共的声明
- 私有的声明
标准中注册的声明 (建议但不强制使用) :
- iss: jwt签发者
- sub: jwt所面向的用户
- aud: 接收jwt的一方
- exp: jwt的过期时间,这个过期时间必须要大于签发时间
- nbf: 定义在什么时间之前,该jwt都是不可用的.
- iat: jwt的签发时间
- jti: jwt的唯一身份标识,主要用来作为一次性token,从而回避时序攻击。
公共的声明 : 公共的声明可以添加任何的信息,一般添加用户的相关信息或其他业务需要的必要信息.但不建议添加敏感信息,因为该部分在客户端可解密.
私有的声明 : 私有声明是提供者和消费者所共同定义的声明,一般不建议存放敏感信息,因为base64是对称解密的,意味着该部分信息可以归类为明文信息。
定义定义一个payload(载荷):
{
"exp": "1234567890",
"name": "John Doe",
"user_id":99
}
然后将其进行base64编码,得到JWT的第二部分。
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9
1.1.3、signature(签证)
JWT的第三部分是一个签证信息,这个签证信息由三部分组成:
- header (base64后的)
- payload (base64后的)
- secret
这个部分需要base64加密后的header和base64加密后的payload使用.连接组成的字符串,然后通过header中声明的加密方式进行加盐secret组合加密,然后就构成了jwt的第三部分。
// javascript
var encodedString = base64UrlEncode(header) + '.' + base64UrlEncode(payload);
var signature = HMACSHA256(encodedString, 'secret'); // TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ
将这三部分用.连接成一个完整的字符串,构成了最终的jwt:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ
注意:secret是保存在服务器端的,jwt的签发生成也是在服务器端的,secret就是用来进行jwt的签发和jwt的验证,所以,它就是你服务端的私钥,在任何场景都不应该流露出去。一旦客户端得知这个secret, 那就意味着客户端是可以自我签发jwt了。
二、jwt开发流程
使用jwt认证的开发流程,就是两部分
- 第一部分: 签发token的过程,登录做的
用户携带用户名和密码,访问我,我们校验通过,生成token串,返回给前端 - 第二部分:token认证过程,登录认证时使用,其实就是咱们之前讲的认证类,在认证类中完成对token的认证操作
用户访问我们需要登陆后才能访问的接口,必须携带我们签发的token串(请求头)
我们取出token,验证该token是否过期,是否被篡改,是否是伪造的
如果正常,说明荷载中的数据,就是安全的,可以根据荷载中的用户id,查询出当前登录用户,放到request中即可
三、drf - jwt的快速使用
django + drf 框架中,使用jwt来做登录认证
3.1、jwt的快速使用的两种方法
-
使用第三方模块
-django-rest-framework-jwt:https://github.com/jpadilla/django-rest-framework-jwt
这个是老版本的,已经不维护了,在django框架3.x以上的版本已经不能使用了,只能3.x及以下版本使用-djangorestframework-simplejwt:https://github.com/jazzband/djangorestframework-simplejwt
新的版本 -
我们可以自己封装
参考链接: https://gitee.com/liuqingzheng/rbac_manager/tree/master/libs/lqz_jwt
3.2、jwt的安装
pip安装: pip3.9 install djangorestframework-jwt
3.3、jwt的签发过程
签发过程(是快速签发),必须是auth的user表(这个是别人已经帮您写好了)
写登录接口 -------》 基于auth的user表签发的
- 路由层代码
from django.contrib import admin
from django.urls import path
# 导入jwt签发的模块
from rest_framework_jwt.views import obtain_jwt_token
urlpatterns = [
path('admin/', admin.site.urls),
# 这样写签发加登录接口就有了
path('login/', obtain_jwt_token),
]
- 创建超级用户
在django的tools目录栏点击Run manage.py Task 后的创建创建超级用户
createsuperuser
创建前
创建后
- 创建两个超级用户,密码一样,为什么结果不一样的分析?
补充:
- 密码明文一样,每次加密后都不一样,如何做,动态加盐,动态的盐要保存,跟密码保存在一起
- 有代码,有数据库,没有登录不进去的系统
- postman展示(获取token成功)
- 可以根据返回的token解码出用户信息
import base64
# "token":
# "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoxLCJ1c2VybmFtZSI6InJvb3QiLCJleHAiOjE2ODUzNjEwMzksImVtYWlsIjoiIn0.JQr8OajKGCvT5o4QFca2aWKiK20D4KJPTJ6p15K_c8o"
# 头部
# res = base64.b64decode('eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9')
# print(res) # b'{"typ":"JWT","alg":"HS256"}'
# 载荷
# res = base64.b64decode('eyJ1c2VyX2lkIjoxLCJ1c2VybmFtZSI6InJvb3QiLCJleHAiOjE2ODUzNjEwMzksImVtYWlsIjoiIn0')
# 需要填充=
res = base64.b64decode('eyJ1c2VyX2lkIjoxLCJ1c2VybmFtZSI6InJvb3QiLCJleHAiOjE2ODUzNjEwMzksImVtYWlsIjoiIn0=')
print(res) # b'{"user_id":1,"username":"root","exp":1685361039,"email":""}'
按上面的推导token出现了解决不了:
就是:如果token被获取,模拟发送请求,这个是解决不了的
但是:token不能篡改,不能伪造
如何保证token的安全性? 设置token的过期时间
以上代码实现的过程是使用auth的user表做登录,不需要写额外的代码了
3.4、jwt的认证过程
- 路由层代码
from django.contrib import admin
from django.urls import path
from app01 import views
# 导入jwt签发的模块
from rest_framework_jwt.views import obtain_jwt_token