核心是提取jwt中稳定字段(如sub)与请求上下文组合成唯一、安全、可复用的缓存键,须在验证通过后解析payload获取sub,再拼接路径、参数摘要等,禁用jti/iat/exp等易变字段,且失败时绝不生成缓存键。

缓存键设计结合 OpenResty 动态解析 JWT,核心在于:**从合法 JWT 中安全、稳定地提取业务标识字段,并与请求上下文组合成唯一、可复用、抗冲突的键**。不能直接用原始 token(太长、含签名、易变),也不能忽略用户身份维度(否则不同用户拿到同一缓存,造成数据泄露)。
提取 JWT 有效载荷中的稳定字段
在 access_by_lua_block 或 rewrite_by_lua_block 阶段完成 JWT 解析后,应优先使用 payload 中语义明确、不可篡改、长期稳定的声明:
-
sub(subject):标准字段,通常代表用户唯一 ID(如
"sub": "user_abc123"),最常用也最推荐 -
user_id 自定义字段:若业务签发时显式写入
"user_id": "10086",且保证非空、格式统一,也可直接使用 - 避免使用
jti(单次令牌 ID)或iat/exp(时间戳)——前者每次登录都变,后者导致缓存键随时间漂移,无法复用
组合请求上下文生成复合键
仅靠用户 ID 不足以区分不同资源请求。需叠加当前请求特征,常见组合方式:
-
用户 ID + 请求路径 + 查询参数摘要:
"u:" .. sub .. ":p:" .. ngx.var.uri .. ":q:" .. ngx.md5(ngx.var.args) -
用户 ID + 接口标识 + 版本号(适用于 API 网关场景):
"api:user:" .. sub .. ":v1:profile" - 若接口对所有用户返回相同内容(如公共配置),可省略用户 ID,但需加
public:前缀做语义隔离,避免与用户级缓存混淆
确保解析阶段前置且失败不缓存
缓存键生成必须依赖已通过验证的 JWT。流程顺序不可颠倒:
- 先执行
jwt_obj:verify_jwt_obj(),校验签名、有效期、iss/aud 等;失败则ngx.exit(401),绝不进入缓存逻辑 - 仅当验证成功、payload 可信时,才调用
jwt_obj:load_jwt(token)提取 payload,并从中读取sub等字段 - 若 JWT 解析异常或字段缺失(如 payload 无
sub),应视为非法请求,同样拒绝并跳过缓存
利用共享字典预处理与复用
为避免重复解析和字符串拼接开销,可将键生成逻辑封装为 Lua 函数,并利用 lua_shared_dict 缓存高频组合规则(如固定路径模板):
- 定义共享字典:
lua_shared_dict jwt_cache 5m; - 对通用接口(如
/api/v1/user/profile),预先注册键模板:"u:{sub}:p:/api/v1/user/profile",运行时仅做变量替换 - 注意:不要把整个 JWT 或用户敏感信息存进共享字典,只缓存结构化模板或哈希后的键前缀











