传统session鉴权:
以Django的账号密码登录为例来说明传统的验证鉴权方式是怎么工作的,当我们登录页面输入账号密码提交表单后,会发送请求给服务器,服务器对发送过来的账号密码进行验证鉴权,验证鉴权通过后,把用户信息记录在服务器端(django_session表中),同时返回给浏览器一个sessionid用来唯一标识这个用户,浏览器将sessionid保存在cookie中,之后浏览器的每次请求都一并将sessionid发送给服务器,服务器根据sessionid与记录的信息做对比以验证身份
Token鉴权
Token的鉴权方式就清晰很多了,客户端用自己的账号密码进行登录,服务端验证鉴权,验证鉴权通过生成Token返回给客户端,之后客户端每次请求都将Token放在header里一并发送,服务端收到请求时校验Token以确定访问者身份
使用场景
- session的主要目的是给无状态的HTTP协议添加状态保持,通常在浏览器作为客户端的情况下比较通用。
- 而Token的主要目的是为了鉴权,同时又不需要考虑CSRF防护以及跨域的问题,所以更多的用在专门给第三方提供API的情况下。
- 客户端请求无论是浏览器发起还是其他的程序发起都能很好的支持。所以目前基于Token的鉴权机制几乎已经成了前后端分离架构或者对外提供API访问的鉴权标准,得到广泛使用。
jwt的结构由三个部分组成:
- Header(头部):定义token类型和加密方式
- Payload(数据):定义具体内容,如用户名,发行时间,过期时间等
- Signature(签名)
headers = {
#typ 属性表示令牌类型,这里就是 JWT。
'typ': ' jwt',
# alg 属性表示签名所使用的算法,JWT 签名默认的算法为 HMAC SHA256 , alg 属性值 HS256 就是 HMAC SHA256 算法
'alg': 'HS256'
}
Payload的格式
#常用格式
payload = {
iss:jwt的签发者/发行人;
sub:主题;
aud:接收方;
exp:jwt过期时间;
nbf:jwt生效时间;
iat:签发时间
jti:jwt唯一身份标识,可以避免重放攻击
}
#也可以不必遵循,例:
payload = {
'userid': userid, #用户表id
'user_code': username #用户名
}
Signature格式
#这是对前边header和payload进行签名,以后在发送/接收消息时,如果在数据被更改,则这段token值会变化,可以在每次传送数据时,判断该token是否被修改,由此判断数据是否安全
token = jwt.encode(payload=payload, key=salt, algorithm='HS256', headers=headers).decode('utf-8')
PyJWT原理解析
利用PyJWT生成Token
In [1]: import jwt
In [2]: encoded_jwt=jwt.encode({"username":"knight"},"ak",algorithm='HS256')
这里传了三部分内容给JWT,
- 第一部分是一个Json对象,称为Payload,主要用来存放有效的信息,例如用户名,过期时间等等所有你想要传递的信息
- 第二部分是一个秘钥字串,这个秘钥主要用在下文Signature签名中,服务端用来校验Token合法性,这个秘钥只有服务端知道,不能泄露
- 第三部分指定了Signature签名的算法
# 查看生成的token In [3]: print(encoded_jwt) eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VybmFtZSI6ImtuaWdodCJ9.vkNT0MVrdW5GJhzPLEtVBqGteqEQAMpW9rBH_SDQSP8
JWT生成的Token是一个用两个点(.)分割的长字符串
点分割成的三部分分别是Header头部,Payload负载,Signature签名:
Header.Payload.Signature
JWT是不加密的,任何人都可以读的到其中的信息。
- 其中第一部分Header和
- 第二部分Payload只是对原始输入的信息转成了base64编码,
- 第三部分Signature是用header+payload+secret_key进行加密的结果
可以直接用base64对 Header 和 Payload进行解码得到相应的信息。
##
In [4]: import base64
In [5]: base64.b64decode('eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9')
Out[5]: b'{"typ":"JWT","alg":"HS256"}'
因为JWT不会对结果进行加密,所以不要保存敏感信息在Header或者Payload中,服务端也主要依靠最后的Signature来验证Token是否有效以及有无被篡改
解密Token
In [7]: jwt.decode(encoded_jwt,'ak',algorithms=['HS256'])
Out[7]: {'username': 'knight'}
JWT实践



