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

.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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.15 10:14:55