ASP.NET API授权过滤器通用权限校验逻辑实现咨询
通用权限校验实现方案
核心思路
把AccessToken中的Condition表达式转换成可复用的校验逻辑,分两步处理:
- 优先尝试从**请求上下文(路由/Query参数)**直接校验,避免不必要的数据库查询
- 若无法从上下文完成校验,则查询目标实体后用表达式验证
实现步骤
1. 依赖准备(可选但推荐)
使用System.Linq.Dynamic.Core库解析Condition表达式,省去手动编写复杂解析逻辑的成本。通过NuGet安装:
Install-Package System.Linq.Dynamic.Core
2. 通用表达式校验工具类
封装表达式解析、请求参数校验判断、临时实体校验等通用逻辑:
using System.Linq.Dynamic.Core; using System.Linq.Expressions; using System.Reflection; public static class ConditionValidator { // 将字符串Condition转换为可执行的实体校验Lambda表达式 public static Expression<Func<T, bool>> GetValidationExpression<T>(string condition) { // 统一表达式关键字大小写,避免大小写不兼容问题 var normalizedCondition = condition.Replace("EQ", "eq").Replace("IN", "in"); return DynamicExpressionParser.ParseLambda<T, bool>(null, normalizedCondition); } // 判断当前条件是否能通过请求参数直接校验 public static bool CanValidateFromRequest<T>(string condition, HttpContext context) { var propertyName = ExtractTargetProperty(condition); if (string.IsNullOrEmpty(propertyName)) return false; // 检查路由或Query参数中是否存在对应属性的参数 return context.Request.RouteValues.ContainsKey(propertyName) || context.Request.Query.ContainsKey(propertyName); } // 从条件字符串中提取目标属性名(适配EQ/IN场景,可根据需求扩展) private static string ExtractTargetProperty(string condition) { var lowerCondition = condition.ToLower(); if (lowerCondition.Contains("eq")) return lowerCondition.Split("eq")[0].Trim(); if (lowerCondition.Contains("in")) return lowerCondition.Split("in")[0].Trim(); return null; } // 从请求上下文提取参数并完成校验 public static bool ValidateFromRequest<T>(string condition, HttpContext context) { var propertyName = ExtractTargetProperty(condition); if (string.IsNullOrEmpty(propertyName)) return false; // 获取参数值 string paramValue = null; if (context.Request.RouteValues.TryGetValue(propertyName, out var routeValue)) paramValue = routeValue.ToString(); else if (context.Request.Query.TryGetValue(propertyName, out var queryValue)) paramValue = queryValue.ToString(); // 构造临时实体并赋值,用表达式校验 var tempEntity = Activator.CreateInstance<T>(); var property = typeof(T).GetProperty(propertyName); if (property == null || paramValue == null) return false; var convertedValue = Convert.ChangeType(paramValue, property.PropertyType); property.SetValue(tempEntity, convertedValue); var validator = GetValidationExpression<T>(condition).Compile(); return validator(tempEntity); } }
3. 实现Authorization Filter
在过滤器中整合两种校验场景的逻辑:
using Microsoft.AspNetCore.Mvc.Filters; using Microsoft.EntityFrameworkCore; public class StudentAuthorizationFilter : IAsyncAuthorizationFilter { private readonly YourDbContext _dbContext; // 替换为你的DbContext类型 public StudentAuthorizationFilter(YourDbContext dbContext) { _dbContext = dbContext; } public async Task OnAuthorizationAsync(AuthorizationFilterContext context) { // 1. 获取AccessToken中的Condition(替换为你实际的Token解析逻辑) var accessToken = ParseAccessTokenFromRequest(context.HttpContext); if (string.IsNullOrEmpty(accessToken?.Condition)) { context.Result = new Microsoft.AspNetCore.Mvc.ForbidResult(); return; } // 2. 尝试从请求上下文直接校验 if (ConditionValidator.CanValidateFromRequest<Student>(accessToken.Condition, context.HttpContext)) { var isValid = ConditionValidator.ValidateFromRequest<Student>(accessToken.Condition, context.HttpContext); if (!isValid) context.Result = new Microsoft.AspNetCore.Mvc.ForbidResult(); return; } // 3. 无法从上下文校验,查询数据库实体后验证 var studentIdStr = context.Request.RouteValues["StudentId"]?.ToString(); if (!int.TryParse(studentIdStr, out var studentId)) { context.Result = new Microsoft.AspNetCore.Mvc.BadRequestResult(); return; } var student = await _dbContext.Students.FirstOrDefaultAsync(s => s.Id == studentId); if (student == null) { context.Result = new Microsoft.AspNetCore.Mvc.NotFoundResult(); return; } var validator = ConditionValidator.GetValidationExpression<Student>(accessToken.Condition).Compile(); if (!validator(student)) context.Result = new Microsoft.AspNetCore.Mvc.ForbidResult(); } // 替换为你实际的AccessToken解析逻辑 private accessToken ParseAccessTokenFromRequest(HttpContext context) { // 示例:从请求头获取并解析Token var authHeader = context.Request.Headers["Authorization"].FirstOrDefault()?.Replace("Bearer ", ""); // 此处省略Token解析为accessToken对象的逻辑,直接模拟返回 return new accessToken { Condition = context.Request.Headers["Condition"].FirstOrDefault() }; } }
4. 注册过滤器
在Program.cs中注入DbContext并注册过滤器:
builder.Services.AddDbContext<YourDbContext>(options => options.UseSqlServer(builder.Configuration.GetConnectionString("YourDbConn"))); // 全局注册过滤器,或针对特定Controller/Action单独注册 builder.Services.AddControllers(options => { options.Filters.Add<StudentAuthorizationFilter>(); });
注意事项
- 表达式容错性:如果Condition包含复杂多条件组合,需要优化
ExtractTargetProperty方法,或引入更专业的表达式解析逻辑 - 性能优化:高频接口可缓存解析后的Lambda表达式,避免重复解析开销
- 安全性:确保Condition表达式来源可信,避免注入风险(限制表达式仅允许预定义的属性和操作符)
内容的提问来源于stack exchange,提问作者Rajil
相关产品推荐
相关产品推荐

