IdentityServer4重定向异常及JArray转JToken错误求助
我来帮你搞定这两个集成时遇到的问题,咱们一步步拆解排查:
问题1:认证完成后重定向到/signin-oidc显示空白页
可能的诱因
从你贴的配置代码来看,有几个关键点可能导致这个空白页问题:
- IdentityServer内部外部认证冲突:你的IdentityServer Startup里同时配置了自身的认证服务(
AddIdentityServer)和对接Azure AD的外部OpenID Connect认证。如果你的MVC客户端是直接通过这个IdentityServer完成登录,而非跳转Azure AD,这个额外的外部认证配置会干扰内部的回调处理流程。 - MVC客户端缺失认证中间件:你的客户端项目
Configure方法里没有添加UseAuthentication,这会导致认证流程无法正确执行,回调页面自然无法跳转。 - 自定义HTTPS中间件位置不当:你在IdentityServer里加了强制设置请求Scheme为HTTPS的中间件,如果位置不对,会影响IdentityServer对回调请求的处理。
修复步骤
清理IdentityServer的冲突配置:如果不需要对接Azure AD,先把那段
AddOpenIdConnect的代码注释掉(如果之后需要外部登录,再单独配置,注意给外部登录设置不同的CallbackPath,避免和MVC客户端的/signin-oidc冲突):// 暂时注释掉这段外部认证配置 // services.AddAuthentication() // .AddOpenIdConnect("oidc", "dataVail Login", options => // { // options.SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme; // options.SignOutScheme = IdentityServerConstants.SignoutScheme; // options.Authority = "https://login.microsoftonline.com/d0e2ebcc-0961-45b2-afae-b9ed6728ead7"; // options.ClientId = "f08cc131-72da-4831-b19d-e008024645e4"; // options.UseTokenLifetime = true; // options.CallbackPath = "/signin-oidc"; // options.RequireHttpsMetadata = false; // options.TokenValidationParameters = new TokenValidationParameters // { // NameClaimType = "name", // RoleClaimType = "role" // }; // });给MVC客户端添加认证中间件:在客户端的
Configure方法里,一定要把UseAuthentication放在UseMvc之前,确保认证流程先执行:public void Configure(IApplicationBuilder app, IHostingEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.UseHttpsRedirection(); app.UseStaticFiles(); // 必须添加这一行,启用认证中间件 app.UseAuthentication(); app.UseMvc(routes => { routes.MapRoute( name: "default", template: "{controller=Home}/{action=Index}/{id?}"); }); }调整IdentityServer的HTTPS中间件位置:如果你的环境已经正确处理HTTPS,这个强制设置Scheme的中间件可以直接移除;如果必须保留,确保它在
UseIdentityServer之前运行:app.UseForwardedHeaders(new ForwardedHeadersOptions { ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto }); // 保留的话,放在UseIdentityServer之前 app.Use(async (context, next) => { context.Request.Scheme = "https"; await next.Invoke(); }); app.UseIdentityServer();
问题2:解决空白页后出现Cannot cast Newtonsoft.Json.Linq.JArray to Newtonsoft.Json.Linq.JToken错误
问题根源
这个错误是因为OpenIdConnect客户端在解析用户信息端点返回的Claims时,遇到了数组类型的Claim值(比如role Claim可能返回多个值),而默认的解析逻辑试图把数组当作单个JToken处理,导致类型转换失败。你的客户端开启了options.GetClaimsFromUserInfoEndpoint = true,会从IdentityServer拉取这些额外的Claims,从而触发这个问题。
修复方案
你需要在OpenIdConnect配置中添加自定义的Claim处理规则,把数组类型的Claim转换为多个单个值的Claim,或者提取数组中的元素。修改客户端的AddOpenIdConnect配置:
.AddOpenIdConnect("oidc", options => { options.SignInScheme = "Cookies"; options.Authority = "https://localhost:44392/"; options.RequireHttpsMetadata = false; options.ClientId = "mvc"; options.ClientSecret = "secret"; options.ResponseType = "code id_token"; options.SaveTokens = true; options.GetClaimsFromUserInfoEndpoint = true; options.Scope.Add("offline_access"); // 处理数组类型的Claim,比如role // MapUniqueJsonKey会把数组中的每个元素作为单独的Claim添加 options.ClaimActions.MapUniqueJsonKey("role", "role"); // 如果还有其他可能是数组的自定义Claim,也用同样方式处理 // options.ClaimActions.MapUniqueJsonKey("custom_claim", "custom_claim"); });
另外,检查你的IdentityServer测试用户配置,确保多值Claim是用多个Claim实例定义的,而不是单个Claim带数组值:
// 正确的多值Claim写法 new TestUser { SubjectId = "1", Username = "alice", Password = "password", Claims = new List<Claim> { new Claim("name", "Alice Smith"), new Claim("role", "Admin"), new Claim("role", "User") // 每个角色单独一个Claim } }
内容的提问来源于stack exchange,提问作者Arshath Shameer

