You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

IdentityServer4:如何仅返回当前Scope对应的Claims

解决IdentityServer中资源仅获取对应Scope声明的问题

嘿,这个问题我之前帮好几个开发者捋清楚过——IdentityServer默认不会自动帮你按Scope和资源的关联关系过滤声明,所以才会出现所有Claims都返回给每个资源的情况。结合你提到的配置场景,我给你分步拆解解决方案:

1. 先把资源与Scope的绑定关系搞扎实

首先要确保你定义ApiResource和ApiScope时,关联关系是专属绑定的,不能交叉混放。举个配置示例:

services.AddIdentityServer()
    .AddInMemoryApiResources(new List<ApiResource>
    {
        new ApiResource("resource1", "资源1")
        {
            // 只绑定属于这个资源的Scope
            Scopes = { "scope1" },
            // 明确该资源能拿到的专属声明
            UserClaims = { "claim1", "claim2" }
        },
        new ApiResource("resource2", "资源2")
        {
            Scopes = { "scope2" },
            UserClaims = { "claim3", "claim4" }
        }
    })
    .AddInMemoryApiScopes(new List<ApiScope>
    {
        new ApiScope("scope1", "权限范围1")
        {
            // 这里的声明要和对应资源的UserClaims完全匹配
            UserClaims = { "claim1", "claim2" }
        },
        new ApiScope("scope2", "权限范围2")
        {
            UserClaims = { "claim3", "claim4" }
        }
    });

核心就是:每个资源只认领自己的Scope,每个Scope只维护自己的声明集合,别把其他Scope的声明混进来。

2. 开启IdentityServer的严格资源验证

在IdentityServer的全局配置里,打开严格资源验证和受众声明,让系统自动按资源过滤声明:

services.AddIdentityServer(options =>
{
    // 强制验证资源与Scope的关联,避免跨资源泄露声明
    options.ResourceValidation = ResourceValidationMode.Strict;
    // 确保令牌里包含正确的受众(aud)标识,方便后续资源验证
    options.EmitStaticAudienceClaim = true;
})
// ... 后续的AddInMemoryClients等配置

ResourceValidationMode.Strict是关键,它会让IdentityServer在颁发令牌时,只把当前资源关联的Scope声明放进去,不会把其他Scope的声明塞进来。

3. 客户端请求要精准指定Scope

客户端配置时,虽然可以把多个Scope加到AllowedScopes里,但实际请求令牌时,要根据访问的资源精准请求对应的Scope——比如访问resource1就只请求scope1,访问resource2就只请求scope2。如果客户端一次性请求所有Scope,IdentityServer会按资源拆分令牌,每个令牌对应一个资源的声明。

举个客户端配置的例子:

.AddInMemoryClients(new List<Client>
{
    new Client
    {
        ClientId = "my_client",
        AllowedGrantTypes = GrantTypes.ClientCredentials,
        ClientSecrets = { new Secret("your_secret_here".Sha256()) },
        // 允许访问的所有Scope,但请求时要按需指定
        AllowedScopes = { "scope1", "scope2" }
    }
})

4. 自定义ProfileService做细粒度控制(可选)

如果上述配置还满足不了你的特殊需求(比如某些声明需要动态判断是否返回),可以实现IProfileService手动过滤声明:

public class CustomProfileService : IProfileService
{
    private readonly UserManager<IdentityUser> _userManager;
    private readonly IUserClaimsPrincipalFactory<IdentityUser> _claimsFactory;

    public CustomProfileService(UserManager<IdentityUser> userManager, IUserClaimsPrincipalFactory<IdentityUser> claimsFactory)
    {
        _userManager = userManager;
        _claimsFactory = claimsFactory;
    }

    public async Task GetProfileDataAsync(ProfileDataRequestContext context)
    {
        var user = await _userManager.GetUserAsync(context.Subject);
        var principal = await _claimsFactory.CreateAsync(user);
        var allClaims = principal.Claims.ToList();

        // 根据当前请求的Scope过滤出允许的声明
        var requestedScopes = context.RequestedResources.ApiResources.SelectMany(r => r.Scopes);
        var allowedClaims = new List<Claim>();

        foreach (var scope in requestedScopes)
        {
            switch (scope.Name)
            {
                case "scope1":
                    allowedClaims.AddRange(allClaims.Where(c => c.Type is "claim1" or "claim2"));
                    break;
                case "scope2":
                    allowedClaims.AddRange(allClaims.Where(c => c.Type is "claim3" or "claim4"));
                    break;
            }
        }

        // 去重后返回
        context.IssuedClaims = allowedClaims.DistinctBy(c => c.Type).ToList();
    }

    public async Task IsActiveAsync(IsActiveContext context)
    {
        var user = await _userManager.GetUserAsync(context.Subject);
        context.IsActive = user != null;
    }
}

然后在Startup里注册这个服务:

services.AddScoped<IProfileService, CustomProfileService>();

最后验证一下

配置完后,你可以用IdentityServer的令牌端点获取对应Scope的令牌,然后用JWT解析工具查看令牌内容,确认aud字段是对应的资源ID,且claims里只有该Scope的声明。

内容的提问来源于stack exchange,提问作者ArdAtak

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.20 08:04:27