Django中的JWT 认证机制
什么是JWT
JSON Web Token(JWT)是目前最流行的跨域身份验证解决方案。该token被设计为紧凑且安全的,特别适用于分布式站点的单点登陆(SSO)场景。JWT的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源,也可以增加一些额外的其它业务逻辑所必须的声明信息,该token也可直接被用于认证,也可被加密。
JWT 特点
- 因为json的通用性,JWT是可以进行跨语言支持的。
- 字节占用很小,便于传输.
- 严格的结构化。它自身(在 payload有效载荷 中)就包含了所有与用户相关的验证消息,如用户可访问路由、访问有效期等信息,服务器无需再去连接数据库验证信息的有效性,并且 payload 支持为你的应用而定制化。
JWT 的数据结构
在紧凑的形式中,JSON Web Tokens由逗号分隔的三个部分组成,它们是:
- 头部
- 有效载荷
- 签名
所以,JWT通常格式如下所示:
xxxxx.zzzzz.yyyyy
Header头部
Jheader典型的由两部分组成:声明类型 jwt 和声明加密的算法名称,如下图。
{
"alg": "HS256",
"typ": "JWT"
}
然后将头部进行base64加密,构成了第一部分。
Payload有效载荷
载荷就是存放有效信息的地方。这些有效信息包含三个部分:
- 标准中注册声明
- 公共的声名
- 私有的声明
标准中注册声明:建议但不强制使用,比如:iss (issuer), exp (expiration time), sub (subject), aud (audience)等。
公共的声名:可以随意定义。
私有的声明: 用于在同意使用它们的各方之间共享信息,一般不建议存放敏感信息,因为base64是对称解密的,部分信息可以归类为明文信息。
例子:
{
"sub": "1234567890",
"name": "John Doe",
"admin": true
}
然后对有效载荷进行base64加密,来构成第二部分。
注意,这些信息虽然受到保护,但任何人都可以阅读。除非加密,否则不要将秘密信息放入JWT的有效负载中。
Signature签名
要创建签名部分,你必须采用编码表头,编码的有效载荷,一个秘钥。然后用Header头部里的声明的算法加密给它加密,得到的就是 jwt 的第三部分。
如:
HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
secret
)
注:这一段是最重要的敏感信息,只能在服务端解密,其中secret是保存在服务器端的。
JWT 数据结构例子
JWT的流程图
如何在Django Python 中使用jwt
具体可以看:https://github.com/GetBlimp/django-rest-framework-jwt/blob/master/docs/index.md
配置环境
安装
pip install djangorestframework-jwt
配置settings文件
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
'rest_framework.authentication.SessionAuthentication', # session认证
'rest_framework.authentication.BasicAuthentication', # 基本认证
),
}
JWT_AUTH = {
'JWT_EXPIRATION_DELTA': datetime.timedelta(days=3), # 过期时间
}
实例,在创建完用户之后创建jwt令牌并发送给前端
导包内容:
from rest_framework_jwt.settings import api_settings
在serializers.py文件中的使用:
内容是写在系列化器的create方法里
def create(self, validated_data):
# 把不需要存进去的删掉
del validated_data['pwdagain']
del validated_data['messagecode']
del validated_data['ischecked']
print(validated_data)
# 在Serializer类中重写create方法
user = super().create(validated_data=validated_data)
user.set_password(validated_data['password']) # 密码加密
user.save()
# 手动创建新令牌
jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER
payload = jwt_payload_handler(user)
token = jwt_encode_handler(payload)
# todo 将令牌传送给前端,给对象动态添加属性的方法
user.token = token
return user
在前端ajax请求成功回调函数里,可以获取后端发来的JWT数据,获取完之后需要保存,一个是sessionStrorage,一个是loaclStrorage,其中sessionStrorage是浏览器关闭就失效,loaclStrorage是长期有效;
$.ajax({
// todo 使用ajax提交POST请求
url: 'http://127.0.0.1:8000/users/myusers/',
method: 'POST',
contentType: 'application/json',
data: data,
success:function (data) {
console.log(data);
console.log(typeof (data));
token = data['token'];
localStorage.clear();
// sessionStorage.token = token; //浏览器关闭就失效
localStorage.token = token; //长期有效
console.log('从本地获取的token',localStorage.token);
},
error: function (data) {
console.log(data);
console.log(localStorage.token);
}
})