在典型的Web架构中,用户登录后获得JWT令牌,后续每次请求都会携带这个令牌访问源站。源站需要反复验证签名、解析声明、查询用户状态,即使令牌本身是无状态的,高并发场景下这些计算和网络往返也会成为瓶颈。如果把这些验证逻辑前置到CDN边缘节点,用户请求在距离最近的边缘位置就能完成身份认证,只有合法流量才被放行回源,源站压力会大幅下降。下面围绕边缘计算与JWT验证展开,介绍在CDN节点终结用户身份的具体做法。

传统身份验证架构的压力来源
在单体或集中式源站架构中,每次API请求都要经过负载均衡、应用服务器和认证中间件。JWT验证虽然避免了数据库查询,但签名验签属于非对称加密运算,会消耗可观的CPU资源。当用户规模扩大时,大量动态请求无法被CDN缓存,全部回源,导致源站不得不在认证环节投入更多计算资源。
更棘手的是,CDN原本只负责静态内容的分发,对于携带Authorization头的动态请求通常直接回源。这意味着即使令牌已经过期或签名非法,这些恶意或无效请求依然会到达源站,占用连接、触发验签逻辑,甚至引发拒绝服务风险。中心化验证方式让源站成为性能瓶颈,扩容成本高,而且响应延迟也受到用户与源站物理距离的影响。
边缘计算的出现改变了这一局面。CDN节点具备代码执行能力后,可以把身份验证这类无状态、计算密集但又相对独立的逻辑放到离用户最近的地方完成。合法请求继续回源,非法请求在边缘直接返回401,源站只处理已经通过初步认证的业务流量。
边缘节点实现JWT验证的技术选型
在边缘节点验证JWT,首要问题是选择签名算法。为了不让边缘节点持有私钥,应该采用非对称算法,例如RS256或ES256。认证服务端使用私钥签发令牌,边缘节点只保存公钥,这样即使边缘节点被攻破,攻击者也无法伪造令牌。公钥可以通过JWK或JWKS格式部署,或者由边缘函数从认证服务的JWKS端点定期拉取并缓存。
边缘函数需要解析请求头中的Authorization字段,提取Bearer令牌,然后验证签名是否有效、过期时间是否已过、issuer和audience是否匹配。大多数边缘计算平台都提供Web Crypto API,支持RSA和ECDSA的签名验证。下面是一个Cloudflare Workers风格的示例,展示核心验证流程。
async function handleRequest(request) {
const authHeader = request.headers.get('Authorization');
if (!authHeader || !authHeader.startsWith('Bearer ')) {
return new Response('Missing or invalid Authorization header', { status: 401 });
}
const token = authHeader.substring(7);
const parts = token.split('.');
if (parts.length !== 3) {
return new Response('Malformed JWT', { status: 401 });
}
const publicKeyJwk = await getPublicKeyJwk(); // 缓存公钥
const encoder = new TextEncoder();
const data = encoder.encode(parts[0] + '.' + parts[1]);
const signature = base64UrlDecode(parts[2]);
const cryptoKey = await crypto.subtle.importKey(
'jwk',
publicKeyJwk,
{ name: 'RSASSA-PKCS1-v1_5', hash: 'SHA-256' },
false,
['verify']
);
const valid = await crypto.subtle.verify(
'RSASSA-PKCS1-v1_5',
cryptoKey,
signature,
data
);
if (!valid) {
return new Response('Invalid JWT signature', { status: 401 });
}
const payload = JSON.parse(decodeBase64Url(parts[1]));
const now = Math.floor(Date.now() / 1000);
if (payload.exp && now > payload.exp) {
return new Response('Token expired', { status: 401 });
}
return new Response('Authenticated', { status: 200 });
}
代码中的base64UrlDecode和decodeBase64Url需要根据具体边缘运行时实现,核心逻辑是补齐Base64 URL字符并解码。验证通过后,可以把用户标识写入请求头转发给源站,例如添加X-Authenticated-User头,源站无需再次验签即可识别用户身份。
在实现时还要注意,JWT头部的alg字段不应该被信任用来选择验证算法。边缘函数应当固定使用预先配置的算法,例如只接受RS256,忽略令牌自身声明的算法,这样可以有效防止算法混淆攻击。
缓存与性能优化策略
公钥获取是边缘验证的关键路径。如果每次请求都去认证服务拉取JWKS,不仅增加外部依赖,还会显著提高延迟。更好的做法是把公钥缓存在边缘KV存储或内存中,设置合理的TTL,例如5分钟到1小时,同时预留主动刷新机制以应对密钥轮换。这样可以在保证安全的前提下大幅减少网络往返。
验证结果也可以做短期缓存。对于同一个JWT令牌,在有效期内签名结果是固定的,如果边缘层能够把令牌摘要和验证结果缓存几十秒,就能避免大量重复验签计算。但缓存策略需要谨慎设计,尤其是面对令牌黑名单或用户被禁用的情况,过长的结果缓存可能导致已失效令牌继续通过验证。因此结果缓存TTL不宜过长,并且敏感操作应当绕过缓存实时校验。
边缘函数还应快速拒绝无效令牌。签名错误、格式错误、过期令牌都应该在边缘直接返回401,不产生源站压力。时钟偏差也是常见问题,边缘节点和认证服务可能存在几秒到几十秒的时间差,建议在检查exp时预留60秒左右的时间容差,避免因时钟不同步导致合法用户被误拒。
安全边界与源站协同
边缘节点终结用户身份之后,源站仍然需要维护会话状态和业务权限。边缘验证确认的是令牌真实性和有效期,但并不意味着用户可以访问所有资源。源站应根据用户角色、资源归属关系执行细粒度授权,例如普通用户无法访问管理接口,或者用户只能操作自己的数据。
此外,要严格校验JWT中的iss和aud声明,防止一个服务的令牌被用于另一个服务。短期访问令牌配合刷新令牌可以降低令牌泄露后的风险窗口。边缘层可以只接受短期访问令牌,长期刷新令牌仍由认证服务处理。
对于转账、删除账户、修改权限等高风险操作,即使边缘验证通过,仍建议强制回源进行二次校验。边缘层终结的是一般身份认证,源站保留关键操作的最终控制权。通过边缘验证降低源站常态压力,同时在关键路径上保持强安全策略,两者结合才能构建高效且可靠的JWT验证体系。