如何正确配置IdentityServer4:Cookie登录与API的JWT授权
这个错误的核心原因是你的API验证JWT时指定的受众(Audience)和IdentityServer4发行的access token中的aud字段不匹配。
你当前的API配置里把jwtOptions.Audience设为了客户端ID,但IdentityServer4在Hybrid模式下,如果没有明确定义API资源,发行的access token默认受众是IDP的通用资源标识(也就是你看到的http://localhost:50059/resources),而非客户端ID。这就导致了受众验证失败。
下面是具体的解决步骤,按照顺序配置即可:
1. 在IdentityServer4端定义API资源
首先需要在IDP的配置中添加一个明确的API资源,这样发行的token会把这个资源名称作为受众之一。
添加API资源的配置代码示例:
public static IEnumerable<ApiResource> GetApiResources() { return new List<ApiResource> { // 这里的"your-api-resource-name"是你给API起的唯一标识,比如"product-api" new ApiResource("your-api-resource-name", "Your API Display Name") { // 可选:如果需要包含额外的声明,或者配置签名算法等,可以在这里添加 // UserClaims = { JwtClaimTypes.Name, JwtClaimTypes.Email }, // AllowedAccessTokenSigningAlgorithms = { SecurityAlgorithms.RsaSha256 } } }; }
然后在IDP的Startup.cs中注册这个API资源:
services.AddIdentityServer() .AddInMemoryApiResources(Config.GetApiResources()) // 添加这一行 .AddInMemoryClients(Config.GetClients()) .AddInMemoryIdentityResources(Config.GetIdentityResources()) // 其他现有配置(比如添加测试用户、认证服务等)
2. 更新客户端配置,允许访问该API资源
在IDP的客户端配置中,把刚才定义的API资源名称添加到AllowedScopes列表里,这样客户端才能请求这个资源的权限:
new Client { ClientId = <ClientID>, ClientName = <ClientName>, AllowedGrantTypes = GrantTypes.HybridAndClientCredentials, RequireConsent = true, ClientSecrets = { new Secret(<secret>.Sha256()) }, AllowOfflineAccess = true, RedirectUris = { "http://" + ip + "/signin-oidc" }, PostLogoutRedirectUris = { "http://" + ip + "/signout-callback-oidc" }, AllowedScopes = { IdentityServerConstants.StandardScopes.OpenId, IdentityServerConstants.StandardScopes.Profile, "your-api-resource-name" // 新增这一行,对应API资源的名称 } };
3. 在MVC客户端请求API资源的Scope
回到你的MVC客户端的OpenID Connect配置,添加对这个API资源的Scope请求,这样登录时会获取包含该API权限的access token:
services.AddAuthentication(options => { options.DefaultAuthenticateScheme = "Cookies"; options.DefaultChallengeScheme = "oidc"; }) .AddCookie("Cookies") .AddOpenIdConnect("oidc", options => { options.SignInScheme = "Cookies"; options.Authority = <local IDP server with IdentityServer4>; options.ClientId = <ClientId>; options.ClientSecret = <secret>; options.ResponseType = "code id_token"; options.SaveTokens = true; options.GetClaimsFromUserInfoEndpoint = true; options.Scope.Add("openid"); options.Scope.Add("profile"); options.Scope.Add("offline_access"); options.Scope.Add("your-api-resource-name"); // 新增这一行 });
4. 更新API的JWT Bearer配置
最后,把API的JWT验证配置中的Audience改成你定义的API资源名称,这样就能和token中的aud字段匹配了:
.AddJwtBearer(jwtOptions => { jwtOptions.Authority = <local IDP server with IdentityServer4>; jwtOptions.Audience = "your-api-resource-name"; // 替换成API资源的名称 jwtOptions.SaveToken = true; })
可选(不推荐):关闭受众验证
如果你只是临时测试,不想修改IDP配置,可以在API端关闭受众验证,但这会降低安全性,不建议在生产环境使用:
.AddJwtBearer(jwtOptions => { jwtOptions.Authority = <local IDP server with IdentityServer4>; jwtOptions.SaveToken = true; jwtOptions.TokenValidationParameters = new TokenValidationParameters { ValidateAudience = false }; })
完成以上配置后,重新生成的access token的aud字段会包含你定义的API资源名称,API验证时就能通过受众检查了。
内容的提问来源于stack exchange,提问作者gargaroff

