Identity Server 4客户端自定义声明缺失问题求助
解决Identity Server 4授权码+PKCE模式下自定义声明/范围声明缺失问题
以下是针对你遇到的声明缺失问题的具体解决步骤,按优先级排序:
1. 检查Identity Server的资源配置
首先要确保自定义声明已经关联到对应的身份资源(IdentityResource)或API资源(ApiResource),否则Identity Server不会把声明放入Token中。
配置自定义身份资源(用于身份相关声明)
在Identity Server的Config.cs中添加包含自定义声明的身份资源:
public static IEnumerable<IdentityResource> IdentityResources => new List<IdentityResource> { new IdentityResources.OpenId(), new IdentityResources.Profile(), // 自定义身份资源,关联你的自定义声明 new IdentityResource( name: "mycustomscope", displayName: "自定义权限范围", userClaims: new List<string> { "mycustomclaim" } ) };
配置API资源(用于API访问相关声明)
如果声明是API专属的,需要在Config.cs中配置ApiResource:
public static IEnumerable<ApiResource> ApiResources => new List<ApiResource> { new ApiResource("myapi", "我的业务API") { // 将自定义声明绑定到该API资源 UserClaims = { "mycustomclaim" } } };
2. 确保MVC客户端请求了正确的Scope
在MVC客户端的Startup.cs中,修改OpenIdConnect配置,添加需要的Scope,确保客户端明确请求包含自定义声明的范围:
services.AddAuthentication(options => { options.DefaultScheme = "Cookies"; options.DefaultChallengeScheme = "oidc"; }) .AddCookie("Cookies") .AddOpenIdConnect("oidc", options => { options.Authority = "https://你的IdentityServer地址"; options.ClientId = "你的客户端ID"; options.ResponseType = "code"; options.UsePkce = true; options.SaveTokens = true; // 清除默认Scope,按需添加 options.Scope.Clear(); options.Scope.Add("openid"); options.Scope.Add("profile"); options.Scope.Add("mycustomscope"); // 添加自定义范围 options.Scope.Add("myapi"); // 如果是API资源范围,也要添加 // 关键:映射自定义声明,避免被默认规则过滤 options.ClaimActions.MapUniqueJsonKey("mycustomclaim", "mycustomclaim"); // 开启从UserInfo端点获取声明(如果声明不在ID Token中) options.GetClaimsFromUserInfoEndpoint = true; });
3. 自定义ProfileService确保声明被返回
默认的ProfileService可能不会自动将所有用户声明放入Token,需要自定义实现来主动添加需要的声明:
创建自定义ProfileService
public class CustomProfileService : IProfileService { private readonly UserManager<ApplicationUser> _userManager; private readonly IUserClaimsPrincipalFactory<ApplicationUser> _claimsFactory; public CustomProfileService(UserManager<ApplicationUser> userManager, IUserClaimsPrincipalFactory<ApplicationUser> claimsFactory) { _userManager = userManager; _claimsFactory = claimsFactory; } public async Task GetProfileDataAsync(ProfileDataRequestContext context) { var userId = context.Subject.GetSubjectId(); var user = await _userManager.FindByIdAsync(userId); if (user == null) throw new ArgumentException("用户不存在"); // 获取用户基础声明 var principal = await _claimsFactory.CreateAsync(user); var claims = principal.Claims.ToList(); // 添加自定义声明(可从用户实体或其他数据源获取) claims.Add(new Claim("mycustomclaim", "mycustomclaim")); // 只返回客户端请求的声明(或全部,根据需求调整) context.IssuedClaims = claims.Where(c => context.RequestedClaimTypes.Contains(c.Type) || context.RequestedClaimTypes.Contains("*") ); } public async Task IsActiveAsync(IsActiveContext context) { var userId = context.Subject.GetSubjectId(); var user = await _userManager.FindByIdAsync(userId); context.IsActive = user != null; } }
在Identity Server中注册自定义ProfileService
在Identity Server的Startup.cs中添加注册:
services.AddScoped<IProfileService, CustomProfileService>();
4. 验证授权策略配置
确保MVC客户端的授权策略配置正确,并且在Controller中正确应用:
// Startup.cs中添加授权策略 services.AddAuthorization(options => { options.AddPolicy("mypolicy", policy => { policy.RequireAuthenticatedUser(); policy.RequireClaim("mycustomclaim", "mycustomclaim"); }); }); // Controller中应用策略 [Authorize(Policy = "mypolicy")] public IActionResult Index() { var claims = User.Claims; foreach (var claim in claims) { _logger.LogDebug("{type}: {value}", claim.Type, claim.Value); } return View(); }
关键说明
PersistedGrants表中的声明只是授权数据的存储,不代表会自动传递到客户端,必须确保Identity Server在颁发Token时将声明包含进去。- 如果声明未出现在ID Token中,开启
GetClaimsFromUserInfoEndpoint = true可以让客户端从UserInfo端点拉取完整声明。
内容的提问来源于stack exchange,提问作者Marvinatorrr
相关产品推荐
相关产品推荐

