ASP.NET Core Identity Server 4多自定义域名仅单个可正常请求异常
嘿,我碰到过类似的坑,这本质上是Identity Server(IS4)发行者(issuer)动态生成与验证规则不匹配导致的。让我给你拆解下原因和解决方案:
问题根源
默认情况下,IS4会根据当前请求的Host头自动设置令牌的issuer值。而Azure App Service重启后,第一个请求的域名会被IS4当作"默认"issuer,后续其他域名生成的令牌issuer会是各自的域名,但验证环节(API端)只认启动时那个issuer,所以就出现了invalid_token错误,而且重启后有效域名随机变化——完全是因为首个请求的域名不同导致的。
解决方案
有两种靠谱的解决思路,你可以根据自己的需求选:
方案一:强制IS4使用固定issuer(推荐,配置更简单)
这种方式让所有域名共享同一个issuer,不管用户用哪个域名访问,令牌里的issuer都是固定值,验证时只需要校验这个值就行。
修改Server项目的Identity Server配置
在Program.cs(.NET 6+)或Startup.cs里,配置AddIdentityServer时添加固定的IssuerUri:builder.Services.AddIdentityServer(options => { // 选一个你常用的域名作为固定issuer,比如你的.ch域名 options.IssuerUri = "https://foo.apply-solutions.ch"; }) .AddInMemoryIdentityResources(/* 你的身份资源配置 */) .AddInMemoryApiResources(/* 你的API资源配置 */) .AddInMemoryClients(/* 你的客户端配置 */) .AddAspNetIdentity<ApplicationUser>() .AddSigningCredential(/* 从Key Vault加载的证书 */);统一客户端的Authority配置
在Blazor Client项目的Program.cs里,把Authority设置为这个固定的issuer地址:builder.Services.AddMsalAuthentication(options => { options.ProviderOptions.Authentication.Authority = "https://foo.apply-solutions.ch"; // 回调地址保持相对路径即可,App Service会自动适配当前域名 options.ProviderOptions.Authentication.RedirectUri = "/authentication/login-callback"; options.ProviderOptions.Authentication.PostLogoutRedirectUri = "/authentication/logout-callback"; // 添加固定issuer对应的API scope options.ProviderOptions.DefaultAccessTokenScopes.Add("https://foo.apply-solutions.ch/BlazorApp1.ServerAPI"); });API端验证配置
在Server项目的Program.cs里,确保JWT验证的ValidIssuers包含这个固定issuer:builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddJwtBearer(options => { options.Authority = "https://foo.apply-solutions.ch"; options.TokenValidationParameters = new TokenValidationParameters { ValidateIssuer = true, ValidIssuers = new List<string> { "https://foo.apply-solutions.ch" }, ValidateAudience = true, ValidAudiences = new List<string> { "BlazorApp1.ServerAPI" } }; });
方案二:配置多issuer允许列表(适合需要保留各自域名作为issuer的场景)
如果必须让每个域名的令牌issuer对应自身域名,那就要在所有环节添加完整的有效issuer列表:
更新IS4的API资源配置
在AddInMemoryApiResources里,给API资源添加所有允许的issuer:new ApiResource("BlazorApp1.ServerAPI", "BlazorApp1 Server API") { AllowedIssuers = new List<string> { "https://foo.apply-solutions.ch", "https://foo.apply-solutions.com", "https://blazorapp1server20201008191519.azurewebsites.net" } }API端验证添加所有有效issuer
同样在JWT验证配置里,把所有域名加入ValidIssuers:options.TokenValidationParameters = new TokenValidationParameters { ValidateIssuer = true, ValidIssuers = new List<string> { "https://foo.apply-solutions.ch", "https://foo.apply-solutions.com", "https://blazorapp1server20201008191519.azurewebsites.net" }, ValidateAudience = true, ValidAudiences = new List<string> { "BlazorApp1.ServerAPI" } };客户端验证配置添加所有有效issuer
在Blazor Client的AddMsalAuthentication里,更新令牌验证参数:options.UserOptions.TokenValidationParameters = new TokenValidationParameters { ValidateIssuer = true, ValidIssuers = new List<string> { "https://foo.apply-solutions.ch", "https://foo.apply-solutions.com", "https://blazorapp1server20201008191519.azurewebsites.net" } };
额外注意事项
- 确保Azure App Service里所有域名都正确绑定了SSL证书,并且
WEBSITE_LOAD_CERTIFICATES配置正确加载了IS4的签名证书。 - 检查CORS设置,把所有域名都加入允许列表,避免跨域请求被拦截。
- 部署后可以访问每个域名的
/.well-known/openid-configuration端点,确认issuer值符合你的配置(方案一应该是固定值,方案二是当前域名)。
内容的提问来源于stack exchange,提问作者NPadrutt

