使用包含多个 CN 或域名的 SSL 托管 asp.net 核心 + ReactJS Web 应用程序导致无效的发行者错误

Hosting asp.net core + ReactJS web app with SSL containing multiple CN or domain names is causing invalid issuer error

我在托管使用 asp.net 核心 3.1 和 React 构建的 Web 应用程序时遇到以下问题。

我们为 React 使用了默认的 visual studio 模板。 ASP.NET身份用于身份验证和授权。

只要我们使用为单个域或 CN 颁发的 SSL 证书托管网站,身份验证和授权就会按预期工作。 (例如 example.com)

如果我们使用具有多个 CN 的 SSL 托管网站(例如 example.com、sub1.example.com、sub2.example.com),它适用于任何一个域。 对于其余域,我们得到以下行为:

登录正常。 /connect/token 路径颁发有效令牌。登录后,当我们尝试调用任何 api(所有 api 都托管在 /api 路由下)时,我们会收到 401 未授权错误。 header中的错误描述:

WWW-Authenticate: Bearer error="invalid_token", error_description="The issuer 'https://sub1.example.com' is invalid".

我还尝试解析 jwt.io 上发布的令牌。 iss 字段(发行者)是 https://sub1.example.com 与错误描述完全匹配。我不明白为什么身份引擎拒绝识别它为其发行令牌的发行者。

这是来自 Startup.cs

的相关片段
public void ConfigureServices(IServiceCollection services)
{
    services.AddIdentityServer()
            .AddApiAuthorization<ApplicationUser, ApplicationDbContext>();
    services.AddAuthentication()
            .AddIdentityServerJwt();
}

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    app.UseAuthentication();
    app.UseIdentityServer();
    app.UseAuthorization();
}

有什么想法吗?

发生这种情况的原因可能是从一个 CN 上的 IdentityServer4 实例接收到令牌,并尝试使用另一个 CN 向 IdentityServer4 发出请求来验证它。拒绝令牌的 IdentityServer 组件是 TokenValidator 的 ValidateJwtAsync method. This method passes in the issuer into JwtSecurityTokenHandler's ValidateToken as a property of TokenValidationParameters. The issuer is retrieved from either the issuer configured on the IdentityServerOptions in the 'AddIdentityServer' extension method, or is dynamically generated from the request.

我能想到一种方法来解决验证问题,那就是使用传递给 AddIdentityServer 的委托在 IdentityServerOptions 上设置颁发者。这将导致为所有发行的令牌设置相同的发行者,而不管它是从哪个 CN 访问的。这将使 IdentityServer 成为颁发者信息的单一真实来源,并允许 IdentityServer 知道当令牌进入验证时要验证哪个颁发者。

其他试图维护发行者的解决方案受到 TokenValidator 的严重限制,因为它是一个内部 class,不能被继承并且很容易被一个将验证有效发行者列表的实现替换。此外,配置为具有颁发者 uri 的 IdentityServerOptions 是 registered as a singleton 并且不能更改其值。可以设计其他人为实现,例如尝试使用中间件动态更改 HttpContext 上的主机值(我不确定这是否可能,因为我从未尝试过),但不建议任何违背 IdentityServer4 设计决策的事情。

请检查url http://{url}/.well-known/openid-configuration

这个url应该是真的

以下代码适用于不同的域。 验证启动

 services.AddIdentityServer(options =>
        {
            options.IssuerUri = Configuration["ServerSettings:Authority"].ToString();
            options.PublicOrigin = Configuration["ServerSettings:Authority"].ToString();
        })
            .AddDeveloperSigningCredential()
            .AddInMemoryApiResources(Config.GetApiResources())
            .AddInMemoryIdentityResources(Config.GetIdentityResources())
            .AddInMemoryClients(Config.GetClients())
            .AddProfileService<ProfileService>();

Api 启动

services.AddAuthentication("Bearer")
        .AddIdentityServerAuthentication(options =>
        {
            options.Authority = Configuration["ServerSettings:Authority"].ToString(); //"http://localhost:31864";
            options.RequireHttpsMetadata = false;
            options.ApiName = "api";
        });

在同一个域中工作,但如果是不同的域,你应该指定这个

新的 .Net(.net 核心)是高度可配置和模块化的。通常扩展方法接受一个委托,我们可以用它来配置选项。但是,AddIdentityServerJwt 方法不遵循该约定。

我很久以前就注意到在TokenValidationParameters中有一个叫做ValidIssuers的属性可以通过AddJwtBearer扩展方法进行配置。但是,AddIdentityServerJwt 扩展方法不接受任何选项委托作为参数。

原来有个special way配置选项

services.AddAuthentication()
    .AddIdentityServerJwt();

services.Configure<JwtBearerOptions>(IdentityServerJwtConstants.IdentityServerJwtBearerScheme, options =>
{
    options.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters()
    {
        ValidIssuers = new string[] { "https://sub1.example.com", "https://sub2.example.com", "https://sub3.example.com" }
    };
});

添加了这段代码,问题解决了。配置也可以移动到appsettings.json.