Keycloak与.NET Core 7 JWT权限验证失败问题求助
以下是导致接口无法通过身份验证的关键配置问题及修复方案:
1. Issuer(签发者)不匹配
JWT Token中的iss字段为http://localhost:2011/realms/TestInventoryRealm,但appsettings里的ServerRealm配置的是http://localhost:2011/realms/TestCompanyInventoryRealm,两者不一致会直接触发Issuer验证失败。
修复:
统一Issuer配置,将appsettings中的ServerRealm修改为与Token一致的值:
"Keycloak": { "ServerRealm": "http://localhost:2011/realms/TestInventoryRealm", // 其他配置保持不变 }
2. RSA公钥加载方式错误
Keycloak采用RSA非对称加密签名JWT,但当前代码用SymmetricSecurityKey加载公钥,这会导致签名验证失败。
修复:
替换为RsaSecurityKey加载公钥,修改JWT配置中的IssuerSigningKey:
// 解析RSA公钥 var publicKey = builder.Configuration["Keycloak:PublicKey"]; var rsa = RSA.Create(); rsa.ImportFromPem(publicKey); options.TokenValidationParameters = new() { ValidateIssuer = true, ValidateAudience = true, ValidateLifetime = true, ValidateIssuerSigningKey = true, ValidIssuer = builder.Configuration["Keycloak:ServerRealm"], IssuerSigningKey = new RsaSecurityKey(rsa), // 使用RSA密钥替代对称密钥 };
3. 授权策略名称不匹配
Program.cs中定义的授权策略名称是"create:company",但CompanyController上标注的Policy = "company"不存在,导致策略验证失败。
修复:
统一策略名称,二选一即可:
- 方案一:修改Controller的Policy属性
[Authorize(AuthenticationSchemes = "Bearer", Roles = "Super Admin Role", Policy = "create:company")] public class CompanyController : CustomBaseController { }
- 方案二:修改策略定义(若需保留
company作为策略名)
builder.Services.AddAuthorization(opts =>{ opts.AddPolicy("company", policy =>{ policy.RequireClaim("scope", new[] { "create:company", "get:company", "update:company", "delete:company" }); }); });
4. 未配置Keycloak客户端角色读取逻辑
Keycloak的客户端角色(如Super Admin Role)存储在Token的resource_access.testcompany-inventory-client.roles路径下,但默认JWT Bearer不会自动解析该路径的角色,导致Roles = "Super Admin Role"验证失败。
修复:
通过JwtBearerOptions.Events手动解析角色:
.AddJwtBearer(options =>{ // 其他配置保持不变 options.Events = new JwtBearerEvents { OnTokenValidated = context => { if (context.Principal.Identity is ClaimsIdentity identity) { var resourceAccessClaim = context.Principal.Claims.FirstOrDefault(c => c.Type == "resource_access")?.Value; if (!string.IsNullOrEmpty(resourceAccessClaim)) { var resourceAccessObj = JsonDocument.Parse(resourceAccessClaim); var clientRoles = resourceAccessObj.RootElement.GetProperty("testcompany-inventory-client").GetProperty("roles"); foreach (var role in clientRoles.EnumerateArray()) { identity.AddClaim(new Claim(ClaimTypes.Role, role.GetString())); } } } return Task.CompletedTask; } }; });
5. 认证与授权中间件顺序颠倒
当前代码中UseAuthorization()在UseAuthentication()之前,会导致授权检查先于认证完成,触发失败。
修复:
调整中间件顺序,确保认证在前:
app.UseHttpsRedirection(); app.UseAuthentication(); // 先执行认证 app.UseAuthorization(); // 再执行授权 app.MapControllers().RequireAuthorization();
内容的提问来源于stack exchange,提问作者fatihgun

