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

如何自定义ASP.NET Core中带后缀参数的Authorize授权策略?

解决方案:自定义Authorize属性+动态策略实现后缀式组授权

针对你的需求,我们可以通过自定义AuthorizeAttribute扩展 + 动态授权策略提供者的方式实现,既不用自定义Filter,也能在方法上直接指定后缀参数,同时兼容你已有的默认/回退策略,以及特殊组的全局权限逻辑。

1. 定义自定义授权需求(Requirement)

创建一个承载授权参数的需求类,包含后缀和特殊组配置:

public class DataAuthorizationRequirement : IAuthorizationRequirement
{
    // 需要匹配的AD组后缀
    public string RoleSuffix { get; }
    // 拥有全量权限的特殊组列表
    public IEnumerable<string> SpecialGroups { get; }

    public DataAuthorizationRequirement(string roleSuffix, IEnumerable<string> specialGroups)
    {
        RoleSuffix = roleSuffix ?? throw new ArgumentNullException(nameof(roleSuffix));
        SpecialGroups = specialGroups ?? Enumerable.Empty<string>();
    }
}

2. 自定义AuthorizeAttribute扩展

继承内置AuthorizeAttribute,添加RoleSuffix参数,并自动生成唯一策略标识:

public class DataAuthorizeAttribute : AuthorizeAttribute
{
    public const string PolicyPrefix = "DataRoleSuffix_";

    public string RoleSuffix
    {
        get => Policy.StartsWith(PolicyPrefix) ? Policy.Substring(PolicyPrefix.Length) : string.Empty;
        set => Policy = $"{PolicyPrefix}{value}";
    }
}

这样写[DataAuthorize(RoleSuffix = "_yyy")]时,会自动生成DataRoleSuffix__yyy的策略名,方便后续动态识别。

3. 实现动态授权策略提供者

通过IAuthorizationPolicyProvider动态生成对应后缀的授权策略,避免为每个后缀手动创建:

public class DataAuthorizationPolicyProvider : DefaultAuthorizationPolicyProvider
{
    private readonly IConfiguration _configuration;
    private readonly AuthorizationOptions _options;

    public DataAuthorizationPolicyProvider(IOptions<AuthorizationOptions> options, IConfiguration configuration)
        : base(options)
    {
        _options = options.Value;
        _configuration = configuration;
    }

    public override async Task<AuthorizationPolicy> GetPolicyAsync(string policyName)
    {
        // 优先使用默认策略(比如你的默认/回退策略)
        var policy = await base.GetPolicyAsync(policyName);

        // 识别自定义后缀策略并动态构建
        if (policy == null && policyName.StartsWith(DataAuthorizeAttribute.PolicyPrefix))
        {
            var roleSuffix = policyName.Substring(DataAuthorizeAttribute.PolicyPrefix.Length);
            // 从配置读取特殊组(可配置在appsettings.json)
            var specialGroups = _configuration.GetSection("Authorization:SpecialGroups").Get<string[]>();

            policy = new AuthorizationPolicyBuilder()
                .AddRequirements(new DataAuthorizationRequirement(roleSuffix, specialGroups))
                .Build();

            // 缓存策略,避免重复创建
            _options.AddPolicy(policyName, policy);
        }

        return policy;
    }
}

4. 实现核心授权逻辑处理程序

编写AuthorizationHandler完成权限验证:

  • 先检查用户是否属于特殊组,是则直接通过
  • 否则验证用户AD组是否匹配指定后缀
public class DataAuthorizationHandler : AuthorizationHandler<DataAuthorizationRequirement>
{
    protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, DataAuthorizationRequirement requirement)
    {
        // 获取用户的AD组声明(注意:ClaimType要和你的AD身份验证配置一致,比如"groups"或组SID对应的Type)
        var userGroups = context.User.Claims
            .Where(c => c.Type == "groups")
            .Select(c => c.Value);

        // 特殊组直接授权通过
        if (requirement.SpecialGroups.Any(sg => userGroups.Contains(sg)))
        {
            context.Succeed(requirement);
            return Task.CompletedTask;
        }

        // 验证组后缀匹配
        if (userGroups.Any(g => g.EndsWith(requirement.RoleSuffix, StringComparison.OrdinalIgnoreCase)))
        {
            context.Succeed(requirement);
            return Task.CompletedTask;
        }

        // 不满足条件则授权失败
        context.Fail();
        return Task.CompletedTask;
    }
}

5. 配置服务(Program.cs/Startup.cs)

注册自定义服务,替换默认策略提供者,并保留你原有的默认/回退策略:

// .NET 6+ Program.cs示例
builder.Services.AddAuthorization(options =>
{
    // 保留你原有的默认和回退策略
    options.DefaultPolicy = new AuthorizationPolicyBuilder()
        .AddRequirements(new DataRequirement())
        .Build();
    options.FallbackPolicy = new AuthorizationPolicyBuilder()
        .AddRequirements(new DataRequirement())
        .Build();
});

// 注册自定义授权处理程序
builder.Services.AddScoped<IAuthorizationHandler, DataAuthorizationHandler>();
// 替换默认策略提供者为自定义实现
builder.Services.AddSingleton<IAuthorizationPolicyProvider, DataAuthorizationPolicyProvider>();

6. 在控制器中使用

现在可以直接在方法上使用自定义属性指定后缀:

[ApiController]
public class DataController : ControllerBase
{
    // 回退策略(你的DataRequirement)
    public ActionResult Get1() {}

    // 默认策略(你的DataRequirement)
    [Authorize]
    public ActionResult Get2() {}

    // 自定义后缀授权
    [DataAuthorize(RoleSuffix = "_yyy")]
    public ActionResult Get3() {}

    // 另一个后缀示例
    [DataAuthorize(RoleSuffix = "_zzz")]
    public ActionResult Get4() {}
}

额外补充:AD组Claim处理

如果你的AD组在Claims中仅存储SID而非名称,可以添加IClaimsTransformation将SID转换为组名称:

builder.Services.AddScoped<IClaimsTransformation, GroupNameClaimsTransformation>();

public class GroupNameClaimsTransformation : IClaimsTransformation
{
    private readonly IAdGroupService _adGroupService; // 自定义AD组查询服务

    public GroupNameClaimsTransformation(IAdGroupService adGroupService)
    {
        _adGroupService = adGroupService;
    }

    public async Task<ClaimsPrincipal> TransformAsync(ClaimsPrincipal principal)
    {
        var identity = (ClaimsIdentity)principal.Identity;
        var groupSids = identity.Claims
            .Where(c => c.Type == "groupsid")
            .Select(c => c.Value);

        foreach (var sid in groupSids)
        {
            var groupName = await _adGroupService.GetGroupNameBySidAsync(sid);
            if (!string.IsNullOrEmpty(groupName))
            {
                identity.AddClaim(new Claim("groups", groupName));
            }
        }

        return principal;
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.04 17:45:38