在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本身的数据库中提取此列表,但是在重新启动应用程序之前,不会再获取员工列表的任何新添加内容。任何指针都高度赞赏。
答案 0 :(得分:1)
您需要结合使用Requirement和Authorization 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);
}
}
在我的示例中,我将AppDbContext
和HttpContextAssessor
传递给ThePolicyRequirement
,但是避免传递AuthorizationHandlerContext
,因为在我的情况下,我只需要区域/控制器/动作名称。重要的是我们可以将整个应用程序中几乎所有可用的信息传递给它。
希望这会有所帮助。