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

如何在ASP.NET Core中为单个Action支持多个授权过滤器?

实现支持多过滤器链式尝试的IAuthorizationFilter机制

要实现类似AuthenticationHandler中AuthenticateResult.NoResult()的链式授权逻辑,核心思路是利用HttpContext存储已执行的过滤器记录,让每个过滤器先判断是否能处理当前请求,不能处理则标记已执行并放行给下一个过滤器,所有过滤器都尝试过后仍无匹配则返回401。

1. 编写通用链式授权过滤器基类

先封装通用逻辑,避免每个过滤器重复编写执行记录和循环判断:

public abstract class ChainedAuthorizationFilter : IAuthorizationFilter
{
    public void OnAuthorization(AuthorizationFilterContext context)
    {
        // 从HttpContext获取已执行的过滤器集合,不存在则初始化
        var executedFilters = context.HttpContext.Items["ExecutedAuthorizationFilters"] as HashSet<string> 
                              ?? new HashSet<string>();
        var filterUniqueId = GetType().FullName;

        // 避免重复执行,防止循环
        if (executedFilters.Contains(filterUniqueId))
        {
            // 所有过滤器都已尝试,返回401
            context.Result = new UnauthorizedResult();
            return;
        }

        // 标记当前过滤器已执行
        executedFilters.Add(filterUniqueId);
        context.HttpContext.Items["ExecutedAuthorizationFilters"] = executedFilters;

        // 由子类判断是否能处理当前请求
        if (!CanHandleRequest(context))
        {
            // 不处理当前请求,设置Result为null让后续过滤器继续执行
            context.Result = null;
            return;
        }

        // 执行子类的具体授权逻辑
        HandleAuthorization(context);
    }

    // 子类实现:判断当前过滤器是否适配请求(比如授权头格式)
    protected abstract bool CanHandleRequest(AuthorizationFilterContext context);

    // 子类实现:具体的授权验证逻辑
    protected abstract void HandleAuthorization(AuthorizationFilterContext context);
}

2. 实现具体的授权过滤器

基于基类编写Bearer和ApiKey两个过滤器:

BearerTokenAuthorizationFilter

public class BearerTokenAuthorizationFilter : ChainedAuthorizationFilter
{
    protected override bool CanHandleRequest(AuthorizationFilterContext context)
    {
        var authHeader = context.HttpContext.Request.Headers.Authorization.ToString();
        return !string.IsNullOrEmpty(authHeader) && 
               authHeader.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase);
    }

    protected override void HandleAuthorization(AuthorizationFilterContext context)
    {
        // 提取Bearer Token
        var token = context.HttpContext.Request.Headers.Authorization.ToString()
                           .Substring("Bearer ".Length);
        
        // 执行Token验证逻辑(替换为你的实际验证代码)
        if (IsValidBearerToken(token))
        {
            // 验证通过,不设置Result,继续后续请求流程
            return;
        }

        // 验证失败,返回401
        context.Result = new UnauthorizedResult();
    }

    private bool IsValidBearerToken(string token)
    {
        // 示例验证逻辑,实际需对接你的Token校验服务
        return token == "valid-bearer-token";
    }
}

ApiKeyAuthorizationFilter

public class ApiKeyAuthorizationFilter : ChainedAuthorizationFilter
{
    protected override bool CanHandleRequest(AuthorizationFilterContext context)
    {
        var authHeader = context.HttpContext.Request.Headers.Authorization.ToString();
        return !string.IsNullOrEmpty(authHeader) && 
               authHeader.StartsWith("Basic ", StringComparison.OrdinalIgnoreCase);
    }

    protected override void HandleAuthorization(AuthorizationFilterContext context)
    {
        // 提取并解码Basic格式的ApiKey
        var encodedKey = context.HttpContext.Request.Headers.Authorization.ToString()
                               .Substring("Basic ".Length);
        var decodedKey = Encoding.UTF8.GetString(Convert.FromBase64String(encodedKey));
        
        // 执行ApiKey验证逻辑(替换为你的实际验证代码)
        if (IsValidApiKey(decodedKey))
        {
            // 验证通过,不设置Result,继续后续请求流程
            return;
        }

        // 验证失败,返回401
        context.Result = new UnauthorizedResult();
    }

    private bool IsValidApiKey(string decodedKey)
    {
        // 示例验证逻辑,实际需对接你的ApiKey校验服务
        return decodedKey == "valid-api-key";
    }
}

3. 在Action上使用多过滤器

直接在需要授权的Action上添加多个过滤器标记即可:

[TypeFilter(typeof(BearerTokenAuthorizationFilter))]
[TypeFilter(typeof(ApiKeyAuthorizationFilter))]
public IActionResult ProtectedResource()
{
    return Ok("授权通过,资源已返回");
}

核心逻辑说明

  • 执行记录存储:用HttpContext.Items保存已执行的过滤器全类名,避免重复执行和无限循环
  • 链式流转:当过滤器无法处理当前请求时,设置context.Result = null,MVC框架会自动执行后续的授权过滤器
  • 最终兜底:当所有过滤器都已尝试且无匹配时,最后一个过滤器会触发executedFilters.Contains(filterUniqueId)判断,返回401未授权

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.01 11:46:02