.NET Core 3.1集成Keycloak OIDC实现SSO登录报错排查
我有一个基于.NET Core 3.1开发的旧应用,计划通过Keycloak结合OpenID Connect(OIDC)实现单点登录(SSO)。当前启动应用后可以正常跳转到Keycloak登录页面,但登录成功返回应用页面时出现错误。
现有配置信息
Startup.cs
services.AddAuthorization(options => { options.AddPolicy("Admin", policy => policy.RequireClaim("user_roles", "admin")); options.AddPolicy("Contractor", policy => policy.RequireClaim("user_roles", "contractor")); options.AddPolicy("Users", policy => policy.RequireClaim("user_roles", "users")); options.AddPolicy( "Admin", policyBuilder => policyBuilder.RequireAssertion( context => context.User.HasClaim(claim => claim.Type == "admin" && (claim.Value == "contractor" || claim.Value == "users") )) ); }); services.AddAuthentication(options => { // We check the cookie to confirm that we are authenticated options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme; // When we sign in we will deal out a cookie options.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme; //Use this to check if we are alllowed to do something. options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme; }) .AddOpenIdConnect(options => { options.CallbackPath = "/Index"; options.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme; options.Authority = Configuration["Authentication:oidc:Authority"]; options.ClientId = Configuration["Authentication:oidc:ClientId"]; //options.ResponseType = useLoadTest ? "code id_token token" : "code id_token"; options.RequireHttpsMetadata = false; options.GetClaimsFromUserInfoEndpoint = true; options.SaveTokens = false; options.RemoteSignOutPath = "/protocol/openid-connect/logout"; options.SignedOutRedirectUri = "Redirect-here"; //options.SignedOutRedirectUri = callBackUrl.ToString(); options.ResponseType = "code"; options.Scope.Add("openid"); options.Scope.Add("profile"); }) .AddCookie(setup => setup.ExpireTimeSpan = TimeSpan.FromMinutes(60));
appsettings.json
"Authentication": { "oidc": { "Authority": "http:/keycloak-testing-ip/auth/realms/project", "ClientId": "demo" } }
登录前页面:
登录后页面:
请问我遗漏了哪些配置或步骤?
你的配置存在多个关键问题,以下是具体修正点:
1. 重复定义授权策略导致冲突
你两次添加了名为Admin的授权策略,第二次定义会覆盖第一次,直接导致权限判断逻辑混乱。删除其中不符合需求的策略,比如保留最初的角色判断策略:
services.AddAuthorization(options => { options.AddPolicy("Admin", policy => policy.RequireClaim("user_roles", "admin")); options.AddPolicy("Contractor", policy => policy.RequireClaim("user_roles", "contractor")); options.AddPolicy("Users", policy => policy.RequireClaim("user_roles", "users")); // 删除重复的Admin策略定义 });
2. 错误设置OIDC回调路径
CallbackPath被设置为/Index(业务页面路径),但OIDC回调需要专门路径处理Keycloak返回的授权码,不能直接指向业务页面。建议使用默认的/signin-oidc(框架会自动处理),或者设置专门的回调路径:
.AddOpenIdConnect(options => { // 移除错误的CallbackPath配置,使用默认值 // options.CallbackPath = "/Index"; // ... 其他配置 })
3. Authority URL格式错误
appsettings.json中的Authority少了一个斜杠,http:/应为http://,这会导致应用无法正确获取Keycloak的OIDC元数据:
"Authentication": { "oidc": { "Authority": "http://keycloak-testing-ip/auth/realms/project", "ClientId": "demo" } }
4. 无效的登出重定向URL
SignedOutRedirectUri设置为"Redirect-here"是无效值,需要指定具体的应用内URL,比如首页:
.AddOpenIdConnect(options => { options.SignedOutRedirectUri = "/"; // ... 其他配置 })
5. 未配置Keycloak角色映射
Keycloak的角色默认存储在realm_access.roles嵌套JSON字段中,.NET Core不会自动提取这些角色作为Claim。需要添加Claim转换规则,将角色映射到策略中使用的user_roles类型:
.AddOpenIdConnect(options => { // ... 其他配置 options.ClaimActions.MapJsonKey("user_roles", "realm_access.roles"); options.TokenValidationParameters = new TokenValidationParameters { NameClaimType = "name", RoleClaimType = "user_roles" }; })
6. 中间件配置顺序错误
确保在Configure方法中正确添加认证和授权中间件,且顺序必须在UseRouting之后、UseEndpoints之前:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { // ... 其他中间件(如UseStaticFiles、UseDeveloperExceptionPage等) app.UseRouting(); // 必须先添加认证中间件,再添加授权中间件 app.UseAuthentication(); app.UseAuthorization(); app.UseEndpoints(endpoints => { endpoints.MapControllerRoute( name: "default", pattern: "{controller=Home}/{action=Index}/{id?}"); }); }
7. 错误设置远程登出路径
RemoteSignOutPath被设置为/protocol/openid-connect/logout(Keycloak自身的登出端点),应用的远程登出回调路径应使用默认的/signout-callback-oidc,或与Keycloak客户端配置的"Valid Redirect URIs"和"Backchannel Logout URL"保持一致:
.AddOpenIdConnect(options => { // 使用默认的远程登出回调路径 // options.RemoteSignOutPath = "/protocol/openid-connect/logout"; // ... 其他配置 })
完成以上修正后,重新启动应用即可正常完成SSO流程。
内容的提问来源于stack exchange,提问作者akayh

