.NET Core 3.1/6中如何实现基于请求的动态自定义声明验证?
在.NET Core 3.1/.NET 6中实现动态Claims验证方案
完全可以实现动态的Claims验证,根据请求中的reportname动态调整权限校验逻辑,同时保留AllReportAccess的向后兼容性。下面提供两种可行方案,优先推荐符合ASP.NET Core授权规范的自定义授权策略方案,也可以改造现有自定义ActionFilter快速适配。
方案一:改造现有自定义ClaimsValidationAttribute
如果你想基于现有代码快速改造,可以调整ClaimsValidationAttribute的逻辑,让它动态读取请求中的reportname并校验对应权限:
1. 定义统一的Claim常量工具类
public static class Claims { public const string AllReportAccess = "AllReportAccess"; // 生成报表专属权限的Claim类型 public static string GetReportSpecificClaim(string reportName) => $"{reportName}Access"; }
2. 修改ClaimsValidationAttribute逻辑
移除硬编码的Claim参数,改为动态读取请求中的reportname,并校验AllReportAccess或报表专属权限:
public class ClaimsValidationAttribute : ActionFilterAttribute { public override async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next) { // 根据实际请求方式获取reportname:Query参数或Body参数 string reportName = null; // 示例1:从QueryString获取(如 /api/report?reportname=Report2) reportName = context.HttpContext.Request.Query["reportname"].FirstOrDefault(); // 示例2:从请求Body获取(需开启流缓冲,避免后续模型绑定失败) // if (context.HttpContext.Request.ContentType?.Contains("application/json") == true) // { // context.HttpContext.Request.EnableBuffering(); // var body = await new StreamReader(context.HttpContext.Request.Body).ReadToEndAsync(); // context.HttpContext.Request.Body.Position = 0; // 重置流位置 // var requestModel = JsonSerializer.Deserialize<YourRequestModel>(body); // reportName = requestModel?.ReportName; // } if (string.IsNullOrEmpty(reportName)) { context.Result = new BadRequestObjectResult("reportname 参数为必填项"); return; } var user = context.HttpContext.User; // 校验是否拥有全量权限或当前报表的专属权限 var hasAllAccess = user.HasClaim(c => c.Type == Claims.AllReportAccess); var hasSpecificAccess = user.HasClaim(c => c.Type == Claims.GetReportSpecificClaim(reportName)); if (!hasAllAccess && !hasSpecificAccess) { context.Result = new ForbidResult(); return; } await next(); } }
3. 控制器使用方式
无需再传硬编码参数,直接标记即可:
[ClaimsValidation] public async Task<IActionResult> GetReport() { // 业务逻辑实现 }
方案二:自定义授权策略(推荐)
ASP.NET Core的授权系统更适合权限校验场景,支持依赖注入、扩展性更强,推荐使用这种方案:
1. 定义授权需求和处理程序
首先创建授权需求类(标记需要授权的操作):
public class ReportAccessRequirement : IAuthorizationRequirement { // 可根据需求添加属性,比如权限等级等 }
然后实现授权处理逻辑,动态校验权限:
public class ReportAccessHandler : AuthorizationHandler<ReportAccessRequirement> { protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, ReportAccessRequirement requirement) { var httpContext = context.Resource as HttpContext; if (httpContext == null) { context.Fail(); return; } string reportName = null; // 同样根据实际请求方式获取reportname reportName = httpContext.Request.Query["reportname"].FirstOrDefault(); // Body参数读取方式同方案一,需开启流缓冲 // ... if (string.IsNullOrEmpty(reportName)) { context.Fail(); return; } // 校验权限 var hasAllAccess = context.User.HasClaim(c => c.Type == Claims.AllReportAccess); var hasSpecificAccess = context.User.HasClaim(c => c.Type == Claims.GetReportSpecificClaim(reportName)); if (hasAllAccess || hasSpecificAccess) { context.Succeed(requirement); // 权限校验通过 } else { context.Fail(); // 权限校验失败 } } }
2. 注册授权服务
.NET 6(Program.cs)
var builder = WebApplication.CreateBuilder(args); // 添加授权服务并注册自定义策略 builder.Services.AddAuthorization(options => { options.AddPolicy("ReportAccessPolicy", policy => policy.Requirements.Add(new ReportAccessRequirement())); }); // 注册授权处理程序 builder.Services.AddScoped<IAuthorizationHandler, ReportAccessHandler>(); // 其他服务注册(如Controllers等) builder.Services.AddControllers(); var app = builder.Build(); // 启用授权中间件 app.UseAuthorization(); app.MapControllers(); app.Run();
.NET Core 3.1(Startup.cs)
public void ConfigureServices(IServiceCollection services) { services.AddControllers(); // 注册授权策略 services.AddAuthorization(options => { options.AddPolicy("ReportAccessPolicy", policy => policy.Requirements.Add(new ReportAccessRequirement())); }); // 注册授权处理程序 services.AddScoped<IAuthorizationHandler, ReportAccessHandler>(); } public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { // 其他中间件(如异常处理、静态文件等) // 启用授权中间件 app.UseAuthorization(); app.UseEndpoints(endpoints => { endpoints.MapControllers(); }); }
3. 控制器使用方式
通过Authorize特性指定自定义策略:
[Authorize(Policy = "ReportAccessPolicy")] public async Task<IActionResult> GetReport() { // 业务逻辑实现 }
方案对比与选择
- 改造ActionFilter:适合快速适配现有代码,改动量小,但不属于ASP.NET Core授权管道的标准流程,扩展性较弱。
- 自定义授权策略:符合ASP.NET Core授权规范,支持依赖注入(比如可注入数据库上下文读取动态权限规则),扩展性和可维护性更强,是推荐方案。
至于中间件:如果需要全局统一的权限校验逻辑可以使用,但针对单个接口的权限控制,授权策略或ActionFilter更精准,无需全局拦截所有请求。
内容的提问来源于stack exchange,提问作者191180rk
相关产品推荐
相关产品推荐

