Windows EC2上aws-jwt-verify获取JWKS超时,替代方案咨询
问题背景
我们有一个Express项目,通过aws-jwt-verify npm包实现Token验证路由。代码在Mac与Linux机器上运行正常,但部署至Windows EC2实例并使用pm2进程管理器运行时,出现功能异常并抛出超时错误。
原代码
const { CognitoJwtVerifier } = require('aws-jwt-verify'); class TokenVerificationSvc { constructor() { } async verify(token, type = 'id') { const docClient = new CognitoJwtVerifier({ userPoolId: process.env.SApplicationUsersPool, tokenUse: type, clientId: process.env.SApplicationUsersPoolClient, }); return await docClient.verify(token); } } module.exports = { TokenVerificationSvc };
错误信息
Failed to fetch https://cognito-idp.ap-south-1.amazonaws.com/.well-known/jwks.json: Response time-out (after 1500 ms.)
改进后代码
const { CognitoJwtVerifier } = require('aws-jwt-verify'); const axios = require('axios'); class TokenVerificationSvc { constructor() { } async verify(token, type = 'id') { const jwksResponse = await axios.get(`https://cognito-idp.${process.env.CLOUD_REGION}.amazonaws.com/${process.env.SApplicationUsersPool}/.well-known/jwks.json`) const docClient = new CognitoJwtVerifier({ userPoolId: process.env.SApplicationUsersPool, tokenUse: type, clientId: process.env.SApplicationUsersPoolClient, }); const jwks = jwksResponse.data; docClient.cacheJwks(jwks); return await docClient.verify(token); } } module.exports = { TokenVerificationSvc };
疑问
- 上述实现方式是否合理?
- 是否存在不依赖axios等第三方请求包的替代方案?
解答
关于实现合理性
当前改进方案存在明显不合理的地方,核心问题如下:
- 重复创建Verifier实例:每次调用
verify都新建CognitoJwtVerifier,cacheJwks的缓存完全无法在多次请求间复用,本质还是每次验证都重复发起JWKS请求,没有发挥缓存作用。 - 无错误处理与超时控制:axios请求JWKS时没有异常捕获和超时配置,一旦请求失败会直接中断验证流程。
- 无缓存过期策略:没有对JWKS做持久化缓存,每次验证都重新请求,既增加延迟也浪费资源,还可能触发AWS请求限制。
合理的优化方向:
- 将
CognitoJwtVerifier改为单例,在类初始化时创建一次,而非每次验证都新建。 - 对获取到的JWKS添加本地缓存和过期刷新逻辑(JWKS不会频繁更新,建议设置1小时左右的有效期)。
- 为HTTP请求添加超时、重试和异常捕获,适配Windows EC2的网络环境。
不依赖第三方请求包的替代方案
可以直接使用Node.js内置的https模块获取JWKS,无需引入axios,同时优化整体实现逻辑:
优化后的单例实现(使用内置https)
const { CognitoJwtVerifier } = require('aws-jwt-verify'); const https = require('https'); const { promisify } = require('util'); // 将https.get转为Promise形式,方便异步调用 const httpsGet = promisify(https.get); class TokenVerificationSvc { constructor() { // 初始化Verifier单例 this.verifier = new CognitoJwtVerifier({ userPoolId: process.env.SApplicationUsersPool, tokenUse: 'id', clientId: process.env.SApplicationUsersPoolClient, }); // 缓存JWKS和过期时间 this.cachedJwks = null; this.jwksExpiry = 0; // JWKS缓存有效期(1小时) this.JWKS_TTL = 3600 * 1000; } async fetchJwks() { const url = `https://cognito-idp.${process.env.CLOUD_REGION}.amazonaws.com/${process.env.SApplicationUsersPool}/.well-known/jwks.json`; try { const req = await httpsGet(url, { timeout: 5000 }); // 设置5秒超时 let data = ''; for await (const chunk of req) { data += chunk; } return JSON.parse(data); } catch (err) { throw new Error(`获取JWKS失败: ${err.message}`); } } async ensureJwksCached() { const now = Date.now(); // 缓存不存在或已过期时重新获取 if (!this.cachedJwks || now > this.jwksExpiry) { this.cachedJwks = await this.fetchJwks(); this.jwksExpiry = now + this.JWKS_TTL; // 更新Verifier的JWKS缓存 this.verifier.cacheJwks(this.cachedJwks); } } async verify(token, type = 'id') { await this.ensureJwksCached(); // 支持动态切换token类型 if (type !== this.verifier.options.tokenUse) { this.verifier = new CognitoJwtVerifier({ userPoolId: process.env.SApplicationUsersPool, tokenUse: type, clientId: process.env.SApplicationUsersPoolClient, }); this.verifier.cacheJwks(this.cachedJwks); } return await this.verifier.verify(token); } } module.exports = { TokenVerificationSvc };
更简单的替代方案:修改默认超时
原问题的核心是默认1500ms超时太短,Windows EC2网络延迟更高,直接修改aws-jwt-verify的fetchTimeout参数即可,无需主动获取JWKS:
const verifier = new CognitoJwtVerifier({ userPoolId: process.env.SApplicationUsersPool, tokenUse: 'id', clientId: process.env.SApplicationUsersPoolClient, fetchTimeout: 5000 // 改为5秒超时 });
建议优先尝试这个方案,若仍有问题再考虑主动缓存JWKS。
额外排查点
确认Windows EC2实例的安全组、网络ACL是否允许出站访问Cognito服务地址,这也是常见的网络超时原因。
内容的提问来源于stack exchange,提问作者sandeepmaity
相关产品推荐
相关产品推荐

