IdentityServer4中AccessToken和IdentityToken中包含的Claims构成
贴出主要代码(以下源码的位置位于:IdentityServer4.Services.DefaultClaimsService)
/// <summary> /// Returns claims for an identity token /// </summary> /// <param name="subject">The subject</param> /// <param name="resources">The requested resources</param> /// <param name="includeAllIdentityClaims">Specifies if all claims should be included in the token, or if the userinfo endpoint can be used to retrieve them</param> /// <param name="request">The raw request</param> /// <returns> /// Claims for the identity token /// </returns> public virtual async Task<IEnumerable<Claim>> GetIdentityTokenClaimsAsync(ClaimsPrincipal subject, Resources resources, bool includeAllIdentityClaims, ValidatedRequest request) { Logger.LogDebug("Getting claims for identity token for subject: {subject} and client: {clientId}", subject.GetSubjectId(), request.Client.ClientId); var outputClaims = new List<Claim>(GetStandardSubjectClaims(subject)); outputClaims.AddRange(GetOptionalClaims(subject)); // fetch all identity claims that need to go into the id token if (includeAllIdentityClaims || request.Client.AlwaysIncludeUserClaimsInIdToken) { var additionalClaimTypes = new List<string>(); foreach (var identityResource in resources.IdentityResources) { foreach (var userClaim in identityResource.UserClaims) { additionalClaimTypes.Add(userClaim); } } // filter so we don't ask for claim types that we will eventually filter out additionalClaimTypes = FilterRequestedClaimTypes(additionalClaimTypes).ToList(); var context = new ProfileDataRequestContext( subject, request.Client, IdentityServerConstants.ProfileDataCallers.ClaimsProviderIdentityToken, additionalClaimTypes); await Profile.GetProfileDataAsync(context); var claims = FilterProtocolClaims(context.IssuedClaims); if (claims != null) { outputClaims.AddRange(claims); } } else { Logger.LogDebug("In addition to an id_token, an access_token was requested. No claims other than sub are included in the id_token. To obtain more user claims, either use the user info endpoint or set AlwaysIncludeUserClaimsInIdToken on the client configuration."); } return outputClaims; } /// <summary> /// Returns claims for an identity token. /// </summary> /// <param name="subject">The subject.</param> /// <param name="resources">The requested resources</param> /// <param name="request">The raw request.</param> /// <returns> /// Claims for the access token /// </returns> public virtual async Task<IEnumerable<Claim>> GetAccessTokenClaimsAsync(ClaimsPrincipal subject, Resources resources, ValidatedRequest request) { Logger.LogDebug("Getting claims for access token for client: {clientId}", request.Client.ClientId); // add client_id var outputClaims = new List<Claim> { new Claim(JwtClaimTypes.ClientId, request.Client.ClientId) }; // check for client claims if (request.ClientClaims != null && request.ClientClaims.Any()) { if (subject == null || request.Client.AlwaysSendClientClaims) { foreach (var claim in request.ClientClaims) { var claimType = claim.Type; if (request.Client.PrefixClientClaims) { claimType = "client_" + claimType; } outputClaims.Add(new Claim(claimType, claim.Value, claim.ValueType)); } } } // add scopes foreach (var scope in resources.IdentityResources) { outputClaims.Add(new Claim(JwtClaimTypes.Scope, scope.Name)); } foreach (var scope in resources.ApiResources.SelectMany(x => x.Scopes)) { outputClaims.Add(new Claim(JwtClaimTypes.Scope, scope.Name)); } // a user is involved if (subject != null) { if (resources.OfflineAccess) { outputClaims.Add(new Claim(JwtClaimTypes.Scope, IdentityServerConstants.StandardScopes.OfflineAccess)); } Logger.LogDebug("Getting claims for access token for subject: {subject}", subject.GetSubjectId()); outputClaims.AddRange(GetStandardSubjectClaims(subject)); outputClaims.AddRange(GetOptionalClaims(subject)); // fetch all resource claims that need to go into the access token var additionalClaimTypes = new List<string>(); foreach (var api in resources.ApiResources) { // add claims configured on api resource if (api.UserClaims != null) { foreach (var claim in api.UserClaims) { additionalClaimTypes.Add(claim); } } // add claims configured on scope foreach (var scope in api.Scopes) { if (scope.UserClaims != null) { foreach (var claim in scope.UserClaims) { additionalClaimTypes.Add(claim); } } } } // filter so we don't ask for claim types that we will eventually filter out additionalClaimTypes = FilterRequestedClaimTypes(additionalClaimTypes).ToList(); var context = new ProfileDataRequestContext( subject, request.Client, IdentityServerConstants.ProfileDataCallers.ClaimsProviderAccessToken, additionalClaimTypes.Distinct()); await Profile.GetProfileDataAsync(context); var claims = FilterProtocolClaims(context.IssuedClaims); if (claims != null) { outputClaims.AddRange(claims); } } return outputClaims; }
简易总结:
AccessToken
从ApiResource中的UserClaims和Scopes.UserClaims中提取返回的Claims类型,构建一个上下文,再调用Profile.GetProfileDataAsync()获取,根据上面提取的Claims类型限制最终返回的Claims
IdentityToken
从IdentityResource的UserClaims中提取返回的Claims类型,构建一个上下文,再调用Profile.GetProfileDataAsync()获取,根据上面提取的Claims类型限制最终返回的Claims
在IdentityResource.UserClaims中设置的内容需要将Client.AlwaysIncludeUserClaimsInIdToken设置为true
GetIdentityTokenClaimsAsync的includeAllIdentityClaims参数只有在只请求IdToken的时候会被设置成true,
官方解释:if no access token is requested, then we need to include all the claims in the id token