You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在客户端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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.02 03:05:50