IdentityServer4 Windows认证添加自定义声明失败求助
你的问题核心在于手动修改User身份并重新登录的方式破坏了IdentityServer4的标准认证流程,导致客户端无法完成令牌验证。Windows认证的用户身份需要由IdentityServer通过规范流程处理,自定义声明应该通过IProfileService来注入,而非直接篡改User对象。下面是具体的解决方案:
1. 实现自定义ProfileService补充声明
ProfileService是IdentityServer官方提供的扩展用户声明的入口,我们可以在这里读取Windows认证的用户信息,并添加自定义属性:
using IdentityServer4.Services; using System.Security.Claims; using System.Security.Principal; public class CustomProfileService : IProfileService { public async Task GetProfileDataAsync(ProfileDataRequestContext context) { // 获取当前用户的Windows身份实例 var windowsIdentity = context.Subject.Identity as WindowsIdentity; if (windowsIdentity != null) { // 添加自定义声明 var customClaims = new List<Claim> { new Claim("devhomepage", "www.devsite.com", ClaimValueTypes.String) }; context.IssuedClaims.AddRange(customClaims); } // 保留原有的Windows认证声明(比如AD用户名、SID等) context.IssuedClaims.AddRange(context.Subject.Claims); } public async Task IsActiveAsync(IsActiveContext context) { // 简单标记用户为活跃状态,可根据业务需求调整判断逻辑 context.IsActive = true; } }
2. 注册ProfileService到IdentityServer
在IdentityServer的Startup.cs中,将自定义ProfileService注册到服务容器,同时确保启用Windows认证支持:
public void ConfigureServices(IServiceCollection services) { services.AddControllersWithViews(); services.AddIdentityServer() .AddDeveloperSigningCredential() .AddInMemoryIdentityResources(Config.IdentityResources) .AddInMemoryApiScopes(Config.ApiScopes) .AddInMemoryClients(Config.Clients) .AddProfileService<CustomProfileService>() // 注册自定义ProfileService .AddWindowsAuthentication(); // 启用Windows认证中间件 }
3. 配置Identity资源与客户端权限
需要确保自定义声明对应的Identity资源被正确配置,并且客户端有权限访问:
在Config.cs中添加自定义Identity资源
可以单独创建一个包含自定义声明的资源,或者将声明合并到默认的Profile scope中:
public static IEnumerable<IdentityResource> IdentityResources => new List<IdentityResource> { new IdentityResources.OpenId(), new IdentityResources.Profile(), // 添加自定义资源,包含我们的devhomepage声明 new IdentityResource( name: "custom_profile", displayName: "Custom User Profile", userClaims: new[] { "devhomepage" }) };
更新客户端配置的AllowedScopes
把自定义资源添加到客户端的允许访问列表中:
new Client { ClientId = "mvc", ClientName = "MVC Client", AllowedGrantTypes = GrantTypes.HybridAndClientCredentials, ClientSecrets = { new Secret("secret".Sha256()) }, RedirectUris = {"http://localhost:60640/signin-oidc"}, PostLogoutRedirectUris = {"http://localhost:60640/signout-callback-oidc"}, AllowedScopes = new List<string> { IdentityServerConstants.StandardScopes.OpenId, IdentityServerConstants.StandardScopes.Profile, "custom_profile" // 加入自定义资源权限 }, AllowOfflineAccess = true, RequireConsent = false }
4. 修正登录逻辑,移除手动SignIn代码
你原来的手动创建ClaimsIdentity并调用HttpContext.SignInAsync(User)的做法是错误的,这会覆盖IdentityServer的原始认证上下文。正确的做法是让IdentityServer自动处理Windows认证后的声明注入,所以登录逻辑应该简化为:
// 移除所有手动添加Identity和SignInAsync的代码,直接跳转到目标页面 return RedirectToLocal(returnUrl); // 若使用自定义登录页,需确保已通过[Authorize(AuthenticationSchemes = WindowsDefaults.AuthenticationScheme)]触发Windows认证
为什么原来的方案无效?
你手动修改User对象并重新登录,会导致IdentityServer生成的令牌基于你新创建的ClaimsIdentity,而非原始的Windows认证Subject。客户端验证令牌时会发现Subject不匹配,从而触发授权失败。通过ProfileService注入声明是IdentityServer的标准扩展方式,不会破坏原有认证流程。
内容的提问来源于stack exchange,提问作者Dev

