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

ASP.NET Core Web API如何实现OR逻辑的双JWT源授权策略?

问题

我有一个可接收两个JWT源令牌的ASP.NET Core Web API,需要定义授权策略满足:

  • 第一个源的令牌必须具备指定角色才能通过
  • 第二个源的任意令牌均可直接通过

我已经在builder.Services.AddAuthorization回调里分别实现了两个规则:

options.AddPolicy("HasTheRightRole", policy =>
        policy.RequireRole("roleNameHere"));
options.AddPolicy("HasTheSecondSource", policy =>
        policy.RequireAuthenticatedUser().AddAuthenticationSchemes("secondSource"));

但用Combine组合这两个策略时是AND逻辑,不符合需求。我通过policy.RequireAssertion实现了OR逻辑:

options.AddPolicy("HasRoleOrPolicy", policy =>
    {
        policy.RequireAssertion(context =>
            context.User.IsInRole("roleNameHere") ||
            context.User.HasClaim(claim => claim.Type == "iss" && claim.Value == "issuerOfSecondSource" && claim.Issuer == "issuerOfSecondSource"));
    });

想知道有没有更优的实现方式?

更优实现方式

方案1:利用多[Authorize]属性实现原生OR逻辑

ASP.NET Core支持在控制器/Action上叠加多个[Authorize]属性,默认逻辑是只要满足其中一个策略即可通过授权,可以直接复用你已定义的两个基础策略,无需额外编写复杂逻辑:

  1. 先完善并注册两个基础策略(给第二个策略补上iss断言,确保是指定源的令牌):
builder.Services.AddAuthorization(options =>
{
    options.AddPolicy("HasTheRightRole", policy =>
        policy.RequireRole("roleNameHere"));
    
    options.AddPolicy("HasTheSecondSource", policy =>
        policy.RequireAuthenticatedUser()
              .AddAuthenticationSchemes("secondSource")
              .RequireClaim("iss", "issuerOfSecondSource"));
});
  1. 在需要授权的控制器或Action上叠加属性:
[Authorize(Policy = "HasTheRightRole")]
[Authorize(Policy = "HasTheSecondSource")]
public IActionResult ProtectedAction()
{
    return Ok();
}

方案2:自定义OR授权需求与处理器(解耦逻辑)

如果需要将OR逻辑封装成可复用的策略,而非依赖属性叠加,可以自定义授权需求和处理器,更符合ASP.NET Core的授权扩展模式:

  1. 定义OR需求类:
public class OrAuthorizationRequirement : IAuthorizationRequirement
{
    public IEnumerable<IAuthorizationRequirement> Requirements { get; }

    public OrAuthorizationRequirement(params IAuthorizationRequirement[] requirements)
    {
        Requirements = requirements;
    }
}
  1. 编写对应的授权处理器:
public class OrAuthorizationHandler : AuthorizationHandler<OrAuthorizationRequirement>
{
    private readonly IPolicyEvaluator _policyEvaluator;

    public OrAuthorizationHandler(IPolicyEvaluator policyEvaluator)
    {
        _policyEvaluator = policyEvaluator;
    }

    protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, OrAuthorizationRequirement requirement)
    {
        foreach (var req in requirement.Requirements)
        {
            var childContext = new AuthorizationHandlerContext(new[] { req }, context.User, context.Resource);
            var result = await _policyEvaluator.EvaluateAsync(context.Policy, childContext, context.Resource);
            
            if (result.Succeeded)
            {
                context.Succeed(requirement);
                return;
            }
        }
    }
}
  1. 注册处理器并创建组合策略:
// 注册自定义处理器
builder.Services.AddScoped<IAuthorizationHandler, OrAuthorizationHandler>();

builder.Services.AddAuthorization(options =>
{
    // 拆分单个需求
    var roleRequirement = new RolesAuthorizationRequirement(new[] { "roleNameHere" });
    var secondSourceRequirement = new ClaimsAuthorizationRequirement("iss", new[] { "issuerOfSecondSource" });

    // 创建OR组合策略
    options.AddPolicy("HasRoleOrSecondSource", policy =>
        policy.Requirements.Add(new OrAuthorizationRequirement(roleRequirement, secondSourceRequirement)));
});

方案3:简化现有RequireAssertion写法

如果不想引入复杂逻辑,也可以简化现有断言代码,让逻辑更清晰:

options.AddPolicy("HasRoleOrSecondSource", policy =>
{
    policy.RequireAssertion(context =>
        context.User.IsInRole("roleNameHere") ||
        context.User.HasClaim("iss", "issuerOfSecondSource"));
});

注:HasClaim(string type, string value)重载已能匹配iss声明的值,若需严格验证令牌的签发者(避免伪造iss声明),可保留原claim.Issuer的判断逻辑。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.12 03:42:37