在Python中使用JWT

JWT 是 Json Web Token 的缩写。一般用于用户认证中。

互联网中,常用的用户认证方案一般有两种。

Session认证

session 认证一般流程如下:

1、用户使用浏览器向服务器发送请求认证,带上用户ID和密码

2、服务器认证通过后保存一下用户信息,如用户ID,登录时间,并生成一个 session 会话保存在缓存或数据库中。查找 session 的 key 叫做 session_id

3、服务器把 session_id 下发给浏览器,写入用户的 cookie 或 storage 中

4、随后浏览器的每一次请求都会带上 session_id 传给服务器,服务器收到后,从缓存中查找用户信息,由此得知用户身份。

使用 session 可以方便的管理用户的状态。在集群环境中,为了保证用户请求每一台服务器都能拿到会话,因此 session 不能单独保存在每一台业务机器中,应该使用公共的缓存或数据库集中管理,保证每一台业务机器都能访问到。

Python 生成session 示例如下:

import time
import hashlib
import json
import redis

now = time.time()
user_id = 12345
user_name = "turbobin"
session_id = hashlib.md5("%s%s" %(user_id, now)).hexdigest()
session_data = json.dumps({"user_id": user_id, "user_name": user_name})
redis.set(session_id, session_data, ex=24*60*60)	# 存到redis中

JWT 认证

JWT 的原理是,服务器认证之后发回给用户一个 json 对象,如:

{
    "user_id": 12345,
    "user_name": "turbobin",
    "expire_time": 86400
}

之后客户端与服务器通信的时候都要发回这个 json 对象,服务器完全只靠这个对象认证用户身份。为了防止篡改数据,服务器生成对象时会加上签名

JWT 的结构如下:

头部 + 负载 + 签名

Header.Playload.Signature

Header 部分

Header 是一个 json 对象,描述 JWT 的元数据:

{
  "alg": "HS256",
  "typ": "JWT"
}

alg表示使用的 算法,默认是 HMAC SHA256,简写成 HS256type表示 token 的类型,像 JWT 令牌统一就写成 JWT

Playload

Playload 也是一个 json 对象,用来存放实际需要传递的数据。JWT 规定了 7 个官方字段:

除了官方字段,还可以定义私有字段,如:

{
    "user_id": 12345,
    "user_name": "turbobin"
}

Signature

signature 是对前两部分的签名,防止数据篡改。

签名前需要指定一个密钥 secret,这个密钥只有服务器知道,不能外泄出去,然后按照 Header 中指定的算法对前两部分进行签名

HMACSHA256(
  base64UrlEncode(header) + "." +
  base64UrlEncode(payload),
  secret)

算出签名以后,把 Header、Payload、Signature 三个部分拼成一个字符串,每个部分之间用”点”(.)分隔,就可以返回给用户。

Python 示例:

import jwt		# 需要先 pip install pyjwt
import time

playload = {
    "user_id": 12345,
    "user_name": "turbobin",
    "exp": int(time.time()) + 5*60		# 5分钟后过期
}
secret = "fjahskljrtlkwjegoi23948nkasdjion"
# 加密
jwt_token = jwt.encode(playload, secret, algorithm='HS256')
print("jwt_tokne:", jwt_token)
# 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX25hbWUiOiJ0dXJib2JpbiIsInVzZXJfaWQiOjEyMzQ1LCJleHAiOjE1ODUxMjYzMDh9.pRIimaTYTV-dS_hTVeG1Uo9y5VqLeln9pUNzRrB180M'

# 解密也要用到 secret
data = jwt.decode(jwt_token, seret) 
# {u'user_id': 12345, u'user_name': u'turbobin', u'exp': 1585126308}

如果设置的 exp 过期了,jwt.decode时候就会抛出异常jwt.exceptions.ExpiredSignatureError: Signature has expired

参考

JSON Web Token 入门教程 - 阮一峰的博客

 


关注微信公众账号「曹当家的」,订阅最新文章推送

Table of Contents