导读:本期聚焦于蚂蚁创作的《如何在AI服务中通过RS256非对称加密与JWKS公钥端点实现JWT鉴权?》,敬请观看详情。AI推理接口一旦暴露到公网,JWT鉴权选型就会直接影响安全边界。继续使用HS256对称签名时,签发和验证共用同一个密钥,密钥泄露后攻击者可以伪造任意身份。RS256基于RSA非对称加密,服务端只需持有公钥即可验证令牌真伪,私钥仅用于签发,适合AI服务被多个客户端调用的场景。本文围绕RS256的落地步骤展开,说明如何生成RSA密钥对、将公钥发布为JWKS端点、配置验证库自动拉取公钥并缓存,以及处理kid不匹配和密钥轮换等常见问题。按照这些方法配置后,AI服务可以在不共享私钥的前提下完成可靠的JWT鉴权,显著降低密钥管理风险。

JWT在AI服务鉴权中有两种常见签名方式:HS256和RS256。HS256使用同一个密钥进行签名和验证,而RS256使用RSA非对称密钥对,私钥负责签发,公钥负责验证。对于面向多租户或开放平台的AI推理服务来说,如果采用HS256,客户端和服务端共享同一个密钥,一旦服务端密钥库泄露,攻击者就可以自行签发任意用户身份的令牌。RS256把密钥管理拆分开,AI服务只保存公钥,私钥只存在于签发服务中,即使AI服务被拖库,攻击者也无法伪造合法令牌。

如何在AI服务中通过RS256非对称加密与JWKS公钥端点实现JWT鉴权?

RS256非对称签名在AI服务中的优势

AI服务通常由推理网关、模型服务、任务队列等多个内部组件组成,外部客户端通过API获取模型推理结果。如果所有组件都持有HS256共享密钥,任何一个组件被攻破都会导致全局令牌伪造。RS256可以避免这种横向扩散,因为验证方只需要公钥,没有私钥就无法签发新令牌。公钥可以通过JWKS端点公开分发,任何验证方都能拉取,但只有持有私钥的授权服务才能产生有效签名。

另一个优点是密钥轮换更方便。当需要更换签名密钥时,签发端生成新的RSA密钥对,把新公钥追加到JWKS文档中,并设置新的kid。验证端根据令牌头里的kid自动匹配对应公钥,旧令牌还能用旧公钥验证,新令牌用新公钥验证,整个过程不需要重启推理服务。HS256要做到平滑轮换,需要在所有节点同步更换共享密钥,容易出现短暂鉴权失败。

RS256也有代价:令牌体积会更大,验证计算量高于HMAC。RSA签名验证通常需要几次模幂运算,对于高并发推理请求会带来一定CPU开销。实际部署时可以在网关层做JWT验证并缓存结果,或者使用EdDSA等更轻量算法替代。不过在生态兼容性上,RS256被绝大多数JWT库和API网关支持,仍是AI服务最稳妥的非对称选择。

生成RSA密钥对并构建JWKS公钥端点

先使用OpenSSL生成2048位或4096位RSA私钥。4096位安全性更高,但验证性能稍差,AI推理请求量大时建议用2048位。生成私钥后提取对应公钥,并把公钥的模数n和指数e转换为Base64URL编码,写入JWKS文档。下面命令展示从生成私钥到输出公钥PEM的过程。

# 生成2048位RSA私钥
openssl genpkey -algorithm RSA -out private_key.pem -pkeyopt rsa_keygen_bits:2048

# 从私钥中提取公钥
openssl rsa -in private_key.pem -pubout -out public_key.pem

# 查看公钥内容
cat public_key.pem

JWKS文档是一个JSON对象,包含keys数组。每个公钥需要提供kty、use、alg、kid、n和e字段。其中n是RSA公钥模数的Base64URL编码,e是公钥指数的Base64URL编码,通常为AQAB表示65537。kid是密钥标识符,必须与JWT头部的kid字段一致,否则验证端无法选择对应公钥。

可以使用Python脚本从PEM公钥自动生成JWKS文档,避免手动计算n和e。下面示例读取public_key.pem,解析出n和e并输出JWKS JSON。生成的文档可以托管在Nginx静态目录,或者由AI服务自身的元数据接口返回。

import json
from cryptography.hazmat.primitives import serialization
from base64 import urlsafe_b64encode

def b64url(data: bytes) -> str:
    return urlsafe_b64encode(data).rstrip(b'=').decode('ascii')

with open('public_key.pem', 'rb') as f:
    pub_key = serialization.load_pem_public_key(f.read())

pub_numbers = pub_key.public_numbers()
n = pub_numbers.n
e = pub_numbers.e

n_bytes = n.to_bytes((n.bit_length() + 7) // 8, 'big')
e_bytes = e.to_bytes((e.bit_length() + 7) // 8, 'big')

jwks = {
    "keys": [
        {
            "kty": "RSA",
            "use": "sig",
            "alg": "RS256",
            "kid": "ai-service-signing-key-2024",
            "n": b64url(n_bytes),
            "e": b64url(e_bytes)
        }
    ]
}

print(json.dumps(jwks, indent=2))

把生成的JWKS文档保存为jwks.json并放在可访问的HTTPS地址下。推荐使用单独的公钥端点路径,例如https://auth.ipipp.com/.well-known/jwks.json。这个地址不需要鉴权,但必须保证内容不被篡改,因为验证端会根据这里面的公钥判断令牌合法性。如果攻击者能修改JWKS内容,就可能换成自己的公钥,所以JWKS端点应配置严格的写权限和HTTPS传输。

在AI服务中验证JWT并缓存公钥

验证端接收到JWT后,先解析头部拿到alg和kid,再从JWKS文档中找到kid一致的公钥进行签名验证。常用的Python库包括PyJWT和python-jose,可以配合requests拉取JWKS。下面的示例使用PyJWT和手动缓存公钥的方式,演示在FastAPI依赖中验证令牌。实际项目中可以把JWKS拉取和公钥缓存封装成独立模块。

import jwt
import requests
from jwt import PyJWKClient

JWKS_URL = "https://auth.ipipp.com/.well-known/jwks.json"
AUDIENCE = "ai-inference-service"
ISSUER = "https://auth.ipipp.com/"

jwks_client = PyJWKClient(JWKS_URL)

def verify_token(token: str):
    signing_key = jwks_client.get_signing_key_from_jwt(token)
    payload = jwt.decode(
        token,
        signing_key.key,
        algorithms=["RS256"],
        audience=AUDIENCE,
        issuer=ISSUER,
        options={"verify_exp": True}
    )
    return payload

PyJWKClient内部会缓存拉取到的公钥,默认按kid建立索引,避免每次验证都请求JWKS端点。如果请求量很大,可以在网关层增加进程内缓存并设置合理的TTL,例如5到15分钟。缓存过期后重新拉取,既降低了公钥端点压力,又能在密钥轮换后及时获取新公钥。需要注意,如果验证时出现kid不匹配错误,不要立即清空整个缓存,可以先尝试拉取最新JWKS并重试一次。

JWT验证还应该校验exp、nbf、iss、aud等声明。AI推理接口可能有不同模型对应不同权限,可以在payload中增加自定义声明,比如model_access或quota。验证通过后,把这些声明放入请求上下文中,后续权限判断就能直接使用。不要在AI服务内部继续传递原始JWT字符串,以免令牌泄露到日志或错误信息中。

常见配置问题与密钥轮换策略

密钥轮换时最容易出现的问题是新旧kid不一致。签发端使用新私钥生成JWT时,如果头部kid还是旧值,验证端会错误地选择旧公钥验证,导致签名不匹配。因此每次生成新密钥对时,必须同步生成新的kid并写入JWKS文档。JWKS文档里可以同时保留多个公钥,旧公钥用于验证历史令牌,新公钥用于新签发令牌。一般建议在JWKS中保留两代公钥,等旧令牌全部过期后再移除。

另一个常见问题是JWKS文档字段格式错误。n和e必须是Base64URL编码且不带填充等号,如果直接使用Base64编码或保留等号,部分JWT库会解析失败。验证前可以写单元测试,确认生成的JWKS能被PyJWKClient正常加载。还要注意JWKS URL必须使用HTTPS,HTTP明文传输的公钥存在被中间人替换的风险。

对于AI服务的高并发场景,可以在网关层做短期验证结果缓存。相同JWT在有效期内重复出现时直接返回缓存结果,但缓存键必须包含完整JWT或签名部分,避免只缓存用户ID导致越权。更稳妥的做法是验证通过后仅在单次请求上下文中传递解析出的身份信息,不缓存令牌本身。RSA验证的CPU消耗通常可接受,过度缓存反而会增加令牌被重放的窗口,需根据实际负载权衡。

JWT鉴权RS256非对称加密JWKS公钥端点修改时间:2026-09-25 20:03:19

免责声明:已尽一切努力确保本网站所含信息的准确性。网站作品多为原创整理与精心创作,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们进行处理Email:chomcom@qq.com。
引用或转载本作品时,请注明当前出处:https://www.ipipp.com/html/0925/61841.html,基于非商业用途的前提下,欢迎转载或二创本作品。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。