能否缓存Azure AD B2C中用于签署令牌的公钥?是否安全?
关于Azure AD B2C签名密钥缓存的问题解答
核心结论
- 缓存签名密钥可行且推荐,但不建议存储到环境变量,更适合用内存缓存或专用缓存服务
- Azure AD B2C的签名密钥会发生变化,必须支持自动刷新机制
密钥变化的场景
Azure AD B2C会在以下情况轮换签名密钥:
- 定期自动轮换(通常每几个月执行一次)
- 密钥达到生命周期上限(后台自动过期)
- 租户管理员手动触发密钥轮换
密钥轮换后,新签发的ID Token会使用新密钥签名,旧密钥会在一段时间内保留,用于验证未过期的旧令牌。
为什么不建议用环境变量存储
环境变量属于静态配置,存在明显缺陷:
- 密钥更新后,需要手动修改环境变量并重启服务,无法自动适配轮换
- 无法同时存储多个密钥(轮换期间新旧密钥会共存),可能导致部分令牌验证失败
最佳实践
1. 利用JWKS客户端自带的缓存功能
你使用的jwks-client库本身支持缓存配置,只需初始化时添加参数即可自动处理:
const client = jwksClient({ jwksUri: discoveryKeysEndpoint, cache: true, // 启用缓存 cacheMaxAge: 86400000, // 缓存有效期24小时(单位:毫秒) cacheMaxEntries: 5 // 最多缓存5个密钥 });
该库会自动管理缓存的刷新,当遇到未知的kid时,会主动从JWKS端点拉取最新密钥。
2. 自定义内存缓存(如需更灵活控制)
如果需要手动实现缓存逻辑,可以用内存对象存储密钥并设置过期时间:
let cachedKeys = {}; let cacheExpiry = 0; const getSigningKeys = async (header) => { const now = Date.now(); // 缓存过期或无对应kid时,重新拉取密钥 if (now > cacheExpiry || !cachedKeys[header.kid]) { const response = await fetch(discoveryKeysEndpoint); const jwks = await response.json(); cachedKeys = jwks.keys.reduce((map, key) => { map[key.kid] = key.publicKey || key.rsaPublicKey; return map; }, {}); cacheExpiry = now + 86400000; // 设置24小时后过期 } return cachedKeys[header.kid]; };
3. 处理密钥轮换的边界情况
- 当验证令牌遇到
kid不存在的情况,立即强制刷新缓存后重试验证 - 不要只缓存单个密钥,保留JWKS返回的所有密钥,确保新旧令牌都能正常验证
内容的提问来源于stack exchange,提问作者Gaurav Mantri
相关产品推荐
相关产品推荐

