从数据库加载ASP.Net核心授权策略

Pan*_*are 7 c# authorization asp.net-core-mvc asp.net-core

在ASP.Net Core中,我们在ConfigureServices方法中定义了授权策略,如下所示.

public void ConfigureServices(IServiceCollection services)
{
   services.AddMvc();

   services.AddAuthorization(options =>
   {
        options.AddPolicy("Founders", policy =>
           policy.RequireClaim ("EmployeeNumber", "1", "2", "3", "4", "5"));
   }
 }
Run Code Online (Sandbox Code Playgroud)

我们在控制器动作上使用它,如下所示.

[Authorize("Founders")]
public IActionResult GenerateReport()
{
   return View();
}
Run Code Online (Sandbox Code Playgroud)

这一切都很好,工作得很好.现在我的问题是,如何在代码中定义"创建者"策略(使用上面代码段中的AddPolicy方法),如何从数据库中添加策略详细信息(名称,声明类型,值)?框架中是否提供了任何钩子,我可以使用它从数据库中填充策略?

这里的想法是我应该能够添加新的员工编号列表(在数据库中),并且应该针对该列表评估策略.遗憾的是,我可以从ConfigureServices本身的数据库中提取此列表,但是在重新启动应用程序之前,不会再获取员工列表的任何新添加内容.任何指针都高度赞赏.

Riz*_*iza 12

您需要结合Requirement和Authorization 处理程序,但不是从数据库填充策略,而是执行您想做的任何身份验证机制。

首先创建一个需求类来提供给策略。让我们暂时命名它ThePolicyRequirement,稍后会详细介绍这个类。其次,创建ThePolicyAuthorizationHandler并将其添加为范围服务

services.AddAuthorization(options =>
{
    options.AddPolicy("ThePolicy", policy => policy.Requirements.Add( new ThePolicyRequirement() ));
});

services.AddScoped<IAuthorizationHandler, ThePolicyAuthorizationHandler>();
Run Code Online (Sandbox Code Playgroud)

关键是我们可以在 中注入几乎任何东西ThePolicyAuthorizationHandler,然后我们将这些注入的对象和任何其他可用的对象传递给ThePolicyRequirement,以便它确定用户是否通过身份验证。

例如,这是我ThePolicyAuthorizationHandler的 asp.net core 2 和 3:

public class ThePolicyAuthorizationHandler : AuthorizationHandler<ThePolicyRequirement>
{
    readonly AppDbContext _context;
    readonly IHttpContextAccessor _contextAccessor;

    public ThePolicyAuthorizationHandler(DbContext c, IHttpContextAccessor ca)
    {
        _context = c;
        _contextAccessor = ca;
    }

    protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, ThePolicyRequirement requirement)
    {
        if (context.Resource is AuthorizationFilterContext filterContext)
        {
            var area = (filterContext.RouteData.Values["area"] as string)?.ToLower();
            var controller = (filterContext.RouteData.Values["controller"] as string)?.ToLower();
            var action = (filterContext.RouteData.Values["action"] as string)?.ToLower();
            var id = (filterContext.RouteData.Values["id"] as string)?.ToLower();
            if (await requirement.Pass(_context, _contextAccessor, area, controller, action, id))
            {
                context.Succeed(requirement);                 
            }

        }
        else if (context.Resource is PolicyResource policyResource)
        {
            var pr = policyResource;
            if (await requirement.Pass(_context, _contextAccessor, pr.Area, pr.Controller, pr.Action, pr.Id))
            {
                context.Succeed(requirement);                    
            }
        }               
    }
}
Run Code Online (Sandbox Code Playgroud)


显然更新,.net 5 中的情况有所变化,下面ThePolicyAuthorizationHandler是 .net 5:

public class ThePolicyAuthorizationHandler : AuthorizationHandler<ThePolicyRequirement>
{
    readonly AppDbContext _context;
    readonly IHttpContextAccessor _contextAccessor;

    public ThePolicyAuthorizationHandler(DbContext c, IHttpContextAccessor ca)
    {
        _context = c;
        _contextAccessor = ca;
    }

    protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, ThePolicyRequirement requirement)
    {
        if (context.Resource is AuthorizationFilterContext filterContext)
        {
            var area = (filterContext.RouteData.Values["area"] as string)?.ToLower();
            var controller = (filterContext.RouteData.Values["controller"] as string)?.ToLower();
            var action = (filterContext.RouteData.Values["action"] as string)?.ToLower();
            var id = (filterContext.RouteData.Values["id"] as string)?.ToLower();
            if (await req.Pass(_context, _contextAccessor, area, controller, action, id))
            {
                context.Succeed(req);
            }
        }
        if (context.Resource is DefaultHttpContext httpContext)
        {
            var area = httpContext.Request.RouteValues["area"].ToString();
            var controller = httpContext.Request.RouteValues["controller"].ToString();
            var action = httpContext.Request.RouteValues["action"].ToString();
            var id = httpContext.Request.RouteValues["id"].ToString();
            if (await req.Pass(_context, _contextAccessor, area, controller, action, id))
            {
                context.Succeed(req);
            }
        }               
    }
}
Run Code Online (Sandbox Code Playgroud)

和我的Requirement班级:

public class ThePolicyRequirement : IAuthorizationRequirement
{
    AppDbContext _context;
    IHttpContextAccessor _contextAccessor;

    public async Task<bool> Pass(AppDbContext context, IHttpContextAccessor contextAccessor, string area, string controller, string action, string id)
    {
        _context = context;
        _contextAccessor = contextAccessor;
        
        //authorization logic goes here

        return await Task.FromResult(false);
    }
}
Run Code Online (Sandbox Code Playgroud)

在我的示例中,我将 myAppDbContext和HttpContextAssessorto传递给ThePolicyRequirement,但不要传递 theAuthorizationHandlerContext因为在我的情况下,我只需要区域/控制器/操作名称。重要的是我们几乎可以将整个应用程序中可用的任何信息传递给它。

希望这会有所帮助。