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

使用Microsoft.Identity.Web的RequiredScope验证控制器范围失效问题排查

解决AD B2C环境下[RequiredScope]特性不生效的问题

问题描述

在基于.NET微服务架构的eShopOnContainers示例中,将认证方式从IdentityServer4切换为AD B2C后,基础认证功能正常。但在控制器上添加[Authorize]和[RequiredScope]特性后,即便把指定的scope改为令牌中不存在的内容(如abc123),请求依然能正常通过,[RequiredScope]未起到校验作用。

初始认证配置代码:

builder.Services.AddAuthentication(options =>
{
    options.DefaultAuthenticateScheme = Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerDefaults.AuthenticationScheme;
    options.DefaultChallengeScheme = Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerDefaults.AuthenticationScheme;

}).AddJwtBearer(options =>
{
    options.MetadataAddress = $"<iss URL>.well-known/openid-configuration?p=<tfp>";
    options.Audience = "<aud GUID>";
});

问题原因

默认的JWT Bearer认证仅会验证令牌的签名、过期时间、受众(Audience)等基础合法性,不会自动关联[RequiredScope]特性进行scope校验,因此需要手动配置校验逻辑。

解决方案

方案一:自定义令牌验证事件处理scope校验

修改JWT Bearer配置,添加OnTokenValidated事件,在令牌验证通过后手动校验scope是否匹配[RequiredScope]的要求:

using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;
using System.Security.Claims;

builder.Services.AddAuthentication(options =>
{
    options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
    options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(options =>
{
    options.MetadataAddress = $"<iss URL>.well-known/openid-configuration?p=<tfp>";
    options.Audience = "<aud GUID>";

    options.TokenValidationParameters = new TokenValidationParameters
    {
        ValidateIssuerSigningKey = true,
        ValidateAudience = true,
        ValidateIssuer = true,
        ValidateLifetime = true,
        // 适配AD B2C的声明字段名称
        NameClaimType = ClaimTypes.Name,
        RoleClaimType = ClaimTypes.Role
    };

    // 新增令牌验证后的scope校验逻辑
    options.Events = new JwtBearerEvents
    {
        OnTokenValidated = context =>
        {
            // 获取当前端点要求的scope
            var requiredScopeAttr = context.HttpContext.GetEndpoint()?.Metadata.GetMetadata<RequiredScopeAttribute>();
            if (requiredScopeAttr?.RequiredScopes == null || !requiredScopeAttr.RequiredScopes.Any())
            {
                return Task.CompletedTask;
            }

            // 从令牌中提取已授权的scope(AD B2C通常用"scp"字段存储scope)
            var tokenScopes = context.Principal?.FindAll("scp")?.Select(c => c.Value) ?? Enumerable.Empty<string>();
            var tokenScopeSet = new HashSet<string>(tokenScopes, StringComparer.OrdinalIgnoreCase);

            // 校验是否包含所有要求的scope
            foreach (var scope in requiredScopeAttr.RequiredScopes)
            {
                if (!tokenScopeSet.Contains(scope))
                {
                    context.Fail($"缺少必要的scope: {scope}");
                    break;
                }
            }

            return Task.CompletedTask;
        }
    };
});

方案二:使用授权策略替代[RequiredScope]

如果不想自定义事件处理,可以通过注册授权策略来实现scope校验,控制器上改用策略名称:

// 注册授权策略
builder.Services.AddAuthorization(options =>
{
    options.AddPolicy("ReadScope", policy =>
    {
        policy.RequireAuthenticatedUser();
        // 要求令牌包含"read" scope(AD B2C的scope字段为"scp")
        policy.RequireClaim("scp", "read");
    });

    // 可添加多个不同scope的策略
    options.AddPolicy("WriteScope", policy =>
    {
        policy.RequireAuthenticatedUser();
        policy.RequireClaim("scp", "write");
    });
});

控制器上使用:

[Authorize(Policy = "ReadScope")]
public class ProductsController : ControllerBase
{
    // ...
}

注意事项

  • 确认AD B2C返回的令牌中,scope存储的字段名称:客户端凭证流通常用scp,授权码流可能用scope,需根据实际令牌内容调整代码中的Claim名称。
  • 若使用.NET 7及以上版本,可直接使用policy.RequireScope("read")方法替代RequireClaim,该方法会自动适配不同的scope声明字段。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.21 15:34:43