JWT认证方案与禁用令牌策略
认证方案
1.1 jwt
对比状态保持机制
APP不支持状态保持
状态保持有同源策略, 无法跨服务器传递
不可逆加密
md5 sha1 sha256
主要用于数据认证, 防止数据被修改
消息摘要 MD
通过哈希算法将任意长度内容转为定长内容, 且相同内容的哈希值始终相同, 不同内容的哈希值不同(极小概率出现碰撞)
由于其唯一性, 一般将数据的哈希值称为数据的摘要信息, 称为数据的"指纹", 用于检测数据是否被修改
代表算法 sha1 sha256 md5
缺点
哈希算法是公开的, 如果可以获取到明文, 就可以穷举出使用的算法
消息认证 MA
哈希算法基础上混入秘钥, 防止哈希算法被破解, 避免签名被伪造
代表算法 hmacsha256
JWT一般会采用 消息认证 机制
一般的web应用, 不会将秘钥交给客户端 ,也就表示
客户端不会验签服务器的身份
缺点
一旦秘钥泄露, 仍然可以伪造签名
数字签名
利用非对称加密对摘要信息进行加密, 避免摘要信息被伪造
非对称加密采用秘钥对
公钥和私钥
公钥加密, 私钥解密
私钥加密, 公钥解密
私钥可以推出公钥, 公钥无法推出私钥
发送者使用私钥对数据摘要加密(签名), 接收者使用对应的公钥解密, 然后对数据进行哈希处理, 比对摘要信息是否一致
代表算法 RSA
使用场景
安全级别要求比较高的系统, 如银行等
优点
客户端不会像消息认证一样保存秘钥, 而是保存了非对称加密的公钥, 即使客户端被破解, 公钥被获取, 也无法通过公钥生成合法的签名
缺点
效率低
1.2 PyJWT
安装
pip install PyJWT
import jwtfrom datetime import datetime, timedeltafrom jwt import PyJWTError?# 包装数据 jwt的规范中要求通过exp参数来设置有效期, 要求有效期使用格林尼治时间payload = {‘payload‘: ‘test‘, ‘exp‘: datetime.utcnow() + timedelta(seconds=30)}?key = ‘secret‘# # 生成jwt# token = jwt.encode(payload, key, algorithm=‘HS256‘)# print(token)??token = b‘eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJwYXlsb2FkIjoidGVzdCIsImV4cCI6MTU2MjgwOTkzMn0.BSc0A2ibdjHTlmW7wtWfj5ZGkny8RX8tV12313‘# 验证jwt pyjwt内部对有效期进行了验证, 如果超过时间, 会报错try: ret = jwt.decode(token, key, algorithms=‘HS256‘) print(ret)except PyJWTError as e: print("jwt认证失败")
数字签名
使用openssl 生成RSA秘钥对
# 生成私钥,指定私钥的长度为2048bit 1024基本安全, 2048非常安全openssl genrsa -out rsa_private_key.pem 2048# 根据私钥生成对应的公钥openssl rsa -in rsa_private_key.pem -pubout -out rsa_public_key_2048.pub# 私钥转化成pkcs8格式, 非必须,pkcs8格式解析起来更方便openssl pkcs8 -topk8 -inform PEM -in rsa_private_key.pem -outform PEM -nocrypt > rsa_private_key_pkcs8.pem
import jwt?"""服务器使用私钥生成签名 对数据摘要加密 称为 签名"""# with open(‘rsa_private_key_pkcs8.pem‘, ‘rb‘) as f:# private_key = f.read()# # 生成数字签名# encoded = jwt.encode({‘some‘: ‘payload‘}, private_key, algorithm=‘RS256‘)# print(encoded)??"""客户端用公钥验签"""encoded = b‘eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJzb21lIjoicGF5bG9hZCJ9.S3gxuFvPiYk2752deTDm6qupj53S0b_-WvFZLKnWzLgDTFjFF_uiwmI6GAT1mKaNvWIyxFQ1PMUPxjkdLuJpGbN3hpHM_eKaQNm_RTvY8UUh6tvq8kpH4FAF2WOglwQK9f3nS8R73PrYhQFDHcfSEBWoJJPva_Pb3YEPMawUTPmd8aeS2uma4n9JqaZwCWm1GE-6S0lKNHp7ZWMlxb5E1R_FgSLIiE3qQq-mWsweyMRtsyCBCaB1W6Y24EYuDW0KHu6k6jGZdwwABVuwyKXKVTTf_XvxM3X41ggpY6mkarSXZsF3-Aw_jWOUBHy9VBHfPCeklur6oMfyGT4FQzkcQQ‘??with open(‘rsa_public_key_2048.pub‘, ‘rb‘) as f: public_key = f.read() decoded = jwt.decode(encoded, public_key, algorithms=‘RS256‘) print(decoded)
1.3 JWE
可逆加密
对称加密
代表算法 des 3des aes
快
非对称加密
代表算法 RSA
慢, 不适合大型数据加密
加密时, 一般公钥加密, 私钥解密, 与签名相反
一般私有只有一方持有, 公钥则可以多方持有(公钥公开)
私钥唯一, 使用私钥签名, 公钥验签, 可以保证签名者身份唯一
加密时, 私钥解密, 保证可以解密者唯一
生成方式 openssl
主要用于数据加密
最佳方案JWE
传输的数据使用对称加密, 生成数据密文, 对称加密秘钥是随机的
为了防止数据篡改, 对数据密文进行摘要认证(一般使用消息认证), 摘要认证的秘钥也是随机的
对称加密的秘钥 和 摘要认证的秘钥 使用非对称加密进行处理
JWE的耗时远高于JWS
用于金融领域
安装
pip install authlib
from authlib.jose import JWEfrom authlib.jose import JWE_ALGORITHMS?# 创建JWE对象jwe = JWE(algorithms=JWE_ALGORITHMS)?# 设置头部信息 指定算法protected = {‘alg‘: ‘RSA-OAEP‘, ‘enc‘: ‘A256GCM‘}# 明文数据# payload = b‘hello‘# 获取到非对称加密的公钥# with open(‘rsa_public_key_2048.pub‘, ‘rb‘) as f:# key = f.read()?# 加密数据# s = jwe.serialize_compact(protected, payload, key)# print(s)???s = b‘eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkEyNTZHQ00ifQ.whFm08vAVhN-BvuRq3BXOqUcw3NnFAAHVswHuqc-JQdixCODernvAQdCDQSlEOmJHhNNm_h1bgji2fctSxY-PDnKv17yCX7IJhDzKLY443NysapeYzku8IAOTPuYV-mE5rk0nCP_u76o8i-kAhmV0OgO19WXgDftqL84zypeBIRmV4w5KaTLGWhU7uOpszHokkTcf1TdXChu7dRcStIekb68-FP5ZmOhAKk8azILH871u290LbIDVowp69tARQJzEzAawiQ7kPmj03XtLQEF6SZgrhH585jQK_hh-NkiMVUiW8GCYPdGLKH8WQoaZionZXrK1ISdAc6RSa3cEiwzGA.AA9nf4X2ftxbp8Ec.kHgOI1E.9Oa_IHyqfdLJ1mCzXopI9g‘?with open(‘rsa_private_key_pkcs8.pem‘, ‘rb‘) as f: # 获取非对称加密的私钥 用于解密 key = f.read() data = jwe.deserialize_compact(s, key) jwe_header = data[‘header‘] payload = data[‘payload‘] print(jwe_header, payload)
1.4 refresh token
1.4.1 刷新token实现流程
?
特点
访问令牌虽然使用频繁, 但是有效期短, 只有两个小时
刷新令牌有效期长, 但是访问次数少, 可以减少泄露的风险
1.4.2 登录接口
接口设计
视图逻辑
生成令牌
1.4.3 访问控制
对于所有的接口都需要获取认证信息 使用请求钩子实现
对于指定的接口进行访问控制 使用装饰器
请求钩子和装饰器
1.4禁用令牌
需求场景
用户修改密码, 需要颁发新的token, 禁用还在有效期的旧token
后台封禁用户
逻辑
禁用旧密码的令牌