如何在客户端Program.cs中从SSO API获取公钥配置JWT验证?
问题描述
我正在实现一个采用非对称加密JWT Token Authentication的Single Sign On(SSO)API,且已在API的部分端点成功完成验证测试。现在我想在另一个Web应用项目(客户端)中使用该认证机制,但不知道如何在Program.cs的配置中从SSO API导入或获取公钥。
SSO API Program.cs
builder.Services.AddSingleton<RsaSecurityKey>(provider => { RSA rsa = RSA.Create(); rsa.ImportRSAPublicKey( source: Convert.FromBase64String(builder.Configuration.GetValue<string>("JwtConfig:Asymmetric:PublicKey")), bytesRead: out int _ ); return new RsaSecurityKey(rsa); }); builder.Services.AddAuthentication().AddJwtBearer("Asymmetric", options => { SecurityKey rsa = builder.Services.BuildServiceProvider().GetRequiredService<RsaSecurityKey>(); options.IncludeErrorDetails = true; // great for debugging // Configure the actual Bearer validation options.TokenValidationParameters = new TokenValidationParameters { IssuerSigningKey = rsa, ValidAudience = "jwt-test", ValidIssuer = "jwt-test", RequireSignedTokens = true, RequireExpirationTime = true, // <- JWTs are required to have "exp" property set ValidateLifetime = true, // <- the "exp" will be validated ValidateAudience = true, ValidateIssuer = true, }; options.MapInboundClaims = false; });
返回公钥的端点
[HttpPost] [Route("JWK")] public async Task<string> GetPublicKey() { return _configuration["JwtConfig:Asymmetric:PublicKey"]; }
客户端Web应用Program.cs
builder.Services.AddAuthentication().AddJwtBearer("Asymmetric", options => { options.IncludeErrorDetails = true; // great for debugging // Configure the actual Bearer validation options.TokenValidationParameters = new TokenValidationParameters { IssuerSigningKey = --> some way of fetching the publick key from API (https:.../api/JWK), ValidAudience = "jwt-test", ValidIssuer = "jwt-test", RequireSignedTokens = true, RequireExpirationTime = true, // <- JWTs are required to have "exp" property set ValidateLifetime = true, // <- the "exp" will be validated ValidateAudience = true, ValidateIssuer = true, }; // pt a nu-mi mai schimba claims cu acele link-uri xml options.MapInboundClaims = false; });
请问如何在当前构建阶段获取公钥?这种使用第三方应用提供的JWT认证的配置方式是否正确?
解决方案
一、构建阶段获取公钥的可行方法
1. 配置文件同步(推荐)
直接将SSO的公钥复制到客户端的appsettings.json中,和SSO API保持一致,避免启动时依赖网络请求:
// 客户端appsettings.json { "JwtConfig": { "Asymmetric": { "PublicKey": "你的Base64格式公钥字符串" } } }
然后在客户端Program.cs中复用SSO API的密钥注册逻辑:
builder.Services.AddSingleton<RsaSecurityKey>(provider => { RSA rsa = RSA.Create(); rsa.ImportRSAPublicKey( source: Convert.FromBase64String(builder.Configuration.GetValue<string>("JwtConfig:Asymmetric:PublicKey")), bytesRead: out int _ ); return new RsaSecurityKey(rsa); }); builder.Services.AddAuthentication().AddJwtBearer("Asymmetric", options => { var rsaKey = builder.Services.BuildServiceProvider().GetRequiredService<RsaSecurityKey>(); options.IncludeErrorDetails = true; options.TokenValidationParameters = new TokenValidationParameters { IssuerSigningKey = rsaKey, ValidAudience = "jwt-test", ValidIssuer = "jwt-test", RequireSignedTokens = true, RequireExpirationTime = true, ValidateLifetime = true, ValidateAudience = true, ValidateIssuer = true, }; options.MapInboundClaims = false; });
2. 启动时同步请求SSO API
若必须从API动态获取,可在启动时创建HTTP客户端同步请求公钥,注意添加异常处理:
builder.Services.AddSingleton<RsaSecurityKey>(provider => { using var httpClient = new HttpClient(); try { // 调用SSO的公钥端点(注意你的端点是POST,需匹配请求方式) var publicKeyBase64 = httpClient.PostAsync("https://你的SSO域名/api/JWK", null) .Result.Content.ReadAsStringAsync().Result; RSA rsa = RSA.Create(); rsa.ImportRSAPublicKey( source: Convert.FromBase64String(publicKeyBase64), bytesRead: out int _ ); return new RsaSecurityKey(rsa); } catch (Exception ex) { throw new InvalidOperationException("获取SSO公钥失败", ex); } }); // 后续AddJwtBearer配置同方法1,注入RsaSecurityKey即可
3. 采用JWKS标准自动获取(最规范)
遵循JSON Web Key Set(JWKS)标准,修改SSO的公钥端点返回标准JWKS格式,让客户端框架自动获取和管理密钥:
首先更新SSO的公钥端点:
[HttpGet] [Route(".well-known/jwks.json")] // 使用行业标准路径 public IActionResult GetJwks() { var rsa = RSA.Create(); rsa.ImportRSAPublicKey( source: Convert.FromBase64String(_configuration["JwtConfig:Asymmetric:PublicKey"]), bytesRead: out int _ ); var securityKey = new RsaSecurityKey(rsa); var jwk = new JsonWebKey(securityKey); var jwks = new JsonWebKeySet { Keys = { jwk } }; return Ok(jwks); }
然后客户端Program.cs简化配置:
builder.Services.AddAuthentication().AddJwtBearer("Asymmetric", options => { options.IncludeErrorDetails = true; options.Authority = "https://你的SSO域名"; // SSO服务基础地址 options.MetadataAddress = "https://你的SSO域名/.well-known/jwks.json"; // JWKS端点 options.Audience = "jwt-test"; options.MapInboundClaims = false; // 框架自动处理密钥验证,也可按需自定义TokenValidationParameters options.TokenValidationParameters = new TokenValidationParameters { RequireSignedTokens = true, RequireExpirationTime = true, ValidateLifetime = true, ValidateAudience = true, ValidateIssuer = true, }; });
这种方式支持密钥自动缓存和轮换,符合OAuth2/JWT行业规范,减少手动维护成本。
二、当前配置方式的合理性与优化点
你的核心配置思路是正确的,但存在几个可优化的地方:
- 不要在
TokenValidationParameters中直接硬编码获取公钥的逻辑,应通过依赖注入提供IssuerSigningKey,避免配置阶段执行复杂IO操作。 - SSO的公钥端点使用POST请求不符合行业惯例,这类元数据接口通常用GET请求。
- 若手动获取公钥,必须添加异常处理,防止客户端因网络问题启动失败。
- 优先采用JWKS标准方式,这是跨应用JWT认证的主流实践,扩展性更强。
内容的提问来源于stack exchange,提问作者sTx
相关产品推荐
相关产品推荐

