随着技术的发展,分布式web应用的普及,通过session管理用户登录状态成本越来越高,因此慢慢发展成为token的方式做登录身份校验,然后通过token去取redis中的缓存的用户信息,随着之后jwt的出现,校验方式更加简单便捷化,无需通过redis缓存,而是直接根据token取出保存的用户信息,以及对token可用性校验,单点登录更为简单。
itsdangerous (使用固定密钥/字符串进行加密, jwt 有多种加密方式, 这只是其中一种, 建议先去了解一下)所生成的 token 仍然是可以被破译从而看到 jwt 的 payload(有效负载) 里的数据, 只不过因为破译者并不知道加密的密钥, 也就无法对数据进行篡改, 所以如果是私密的数据, 就不应该使用 jwt 进行传递, 如 账号的密码, 以防止泄露. 如果需要传递私密数据, 解决办法是,对 payload 的数据进行加密,从而杜绝非法破译者看到 payload 内的任何信息,但是目前加密payload的操作不是很普及,在不加密 payload 的前提下, jwt 比较适合进行非受信任端的身份验证, 此时即使接收方破译了 token, 看到了 payload 的数据, 也不会造成太大的影响, 因为数据是无法被篡改的(当接收方将 token 值返回给服务器后, 需要使用相同的密钥进行解密, 所以服务器的密钥一定要保管好), 只要接收方将 token 原封不动的返回给服务器, 那么服务器就可以根据 token 值的内容来确认接收方身份的合法性,而不需要关心接收方是否看到过 payload 的内容.
简而言之, 除非额外对 payload 加密过, 否则就不要在 jwt 中传递不可被第三方获知的私密数据
1.对称加密HMAC【哈希消息验证码】 HS256/HS384/HS512
这种加密方式没有公钥,私钥之分, 也就是只有一个密钥, 这种加密方式适用于: 服务器将生成的jwt发送给接收方, 接收方将其返回给服务器, 服务器解析 jwt, 完成身份验证.
2.非对称加密RSASSA【RSA签名算法】RS256/RS384/RS512
3.ECDSA【椭圆曲线数据签名算法】 ES256/ES384/ES512
环境 python3.6
依赖包 PyJWT
import base64
import jwt
import datetime
class Token(object):
SECRET_KEY = 'it hard to guess!!!'
EXP_DAYS = 30
# 这里可以对错误类型进行定义
TOKEN_EXPIRED = 'TOKEN_EXPIRED'
TOKEN_INVALID = 'TOKEN_INVALID'
TOKEN_REQUIRED = 'TOKEN_REQUIRED'
@staticmethod
def encode_token(user={}):
try:
payload = {
# 过期时间
'exp': datetime.datetime.utcnow() + datetime.timedelta(days=0, seconds=10),
# 发行时间
'iat': datetime.datetime.utcnow(),
# token签发者
'iss': 'zzk',
# user info
'data': user,
"jti": "4f1g23a12aa"
}
"""payload 中一些固定参数名称的意义, 同时可以在payload中自定义参数"""
# iss 【issuer】发布者的url地址
# sub 【subject】该JWT所面向的用户,用于处理特定应用,不是常用的字段
# aud 【audience】接受者的url地址
# exp 【expiration】 该jwt销毁的时间;unix时间戳
# nbf 【not before】 该jwt的使用时间不能早于该时间;unix时间戳
# iat 【issued at】 该jwt的发布时间;unix 时间戳
# jti 【JWT ID】 该jwt的唯一ID编号
return str(jwt.encode(
payload,
Token.SECRET_KEY,
algorithm='HS512',
), 'utf-8')
except Exception as e:
print(e)
return e
@staticmethod
def verify_bearer_token(token):
payload = jwt.decode(token, Token.SECRET_KEY, algorithms=['HS512'])
if payload:
return True, payload
return False, token
if __name__ == '__main__':
user = {'user': 'zzk'}
jwt_token = Token.encode_token(user)
print(jwt_token)
a = base64.b64decode(jwt_token.split('.')[0])
print(a)
state, token = Token.verify_bearer_token(jwt_token)
print(state)
print(token)
解析需要使用到同样的serializer,配置一样的secret key和salt,使用loads方法来解析token。itsdangerous提供了各种异常处理类,用起来也很方便:
如果是SignatureExpired,则可以直接返回过期;
如果是BadSignature,则代表了所有其他签名错误的情况,于是又分为:
能读取到payload:那么这个消息是一个内容被篡改、消息体加密过程正确的消息,secret key和salt很可能泄露了;
不能读取到payload: 消息体直接被篡改,secret key和salt应该仍然安全。
python 对于 jwt 的实现, 已经有了 itsdangerous 这个库做了很好的支撑, 使用起来还是很方便的
itsdangerous 官方文档: https://itsdangerous.readthedocs.io/en/1.1.x/
深入了解Json Web Token之概念篇 https://www.freebuf.com/articles/web/180874.html