如何在ASP.NET Core中为单个Action支持多个授权过滤器?
要实现类似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
相关产品推荐
相关产品推荐

