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

时间:2017-02-02 01:28:50

标签: c# asp.net-core authorization asp.net-core-mvc

在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"));
   }
 }

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

[Authorize("Founders")]
public IActionResult GenerateReport()
{
   return View();
}

这一切都很好并且完美无缺。现在我的问题是,如何在代码中定义“创建者”策略(使用上面代码段中的AddPolicy方法),如何从数据库中添加策略详细信息(名称,声明类型,值)?框架中是否提供了可用于从数据库填充策略的钩子?

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

1 个答案:

答案 0 :(得分:1)

您需要结合使用RequirementAuthorization handlers,但不是为了从数据库中填充策略,而是为了执行所需的身份验证机制。

首先创建一个需求类,以提供给策略。现在,我们将其命名为ThePolicyRequirement,稍后再讲这个类。其次,创建ThePolicyAuthorizationHandler并将其添加为范围服务

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

services.AddScoped<IAuthorizationHandler, ThePolicyAuthorizationHandler>();

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

例如,这是我的ThePolicyAuthorizationHandler

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
                    context.Fail();

            }
            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);
                else
                    context.Fail();
            }
            else
            {
                context.Fail();
            }
        }
    }

和我的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);
        }
    }

在我的示例中,我将AppDbContextHttpContextAssessor传递给ThePolicyRequirement,但是避免传递AuthorizationHandlerContext,因为在我的情况下,我只需要区域/控制器/动作名称。重要的是我们可以将整个应用程序中几乎所有可用的信息传递给它。

希望这会有所帮助。