我正在尝试写一个Policy-based Authorization Handler。处理程序的业务逻辑需要使用通过默认路由传递的当前请求的记录id
。
[Authorize(Roles = "TaskAdmin", Policy = "RecordOwner")]
public IActionResult Index(int id) // <-- Need this id
{
// <snip>
return View();
}
以下是我需要访问id
路由值的类。
public class RecordOwnerHandler : AuthorizationHandler<RecordOwnerRequirement>
{
private readonly ApplicationDbContext dbContext;
public RecordOwnerHandler(ApplicationDbContext dbContext)
{
this.dbContext = dbContext ?? throw new ArgumentNullException(nameof(dbContext));
}
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, RecordOwnerRequirement requirement)
{
if (IsUserAuthorized(context))
{
context.Succeed(requirement);
}
//TODO: Use the following if targeting a version of
//.NET Framework older than 4.6:
// return Task.FromResult(0);
return Task.CompletedTask;
}
private bool IsUserAuthorized(AuthorizationHandlerContext context)
{
//****************************************
// Need the id here...
//****************************************
// Return the result
return true;
}
}
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
services.AddIdentity<ApplicationUser, IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
// *** Add policy for record owner ***
services.AddAuthorization(options =>
{
options.AddPolicy("RecordOwner", policy =>
policy.Requirements.Add(new RecordOwnerRequirement()));
});
// Add application services.
services.AddTransient<IEmailSender, EmailSender>();
// *** Register record owner handler with the DI container ***
services.AddTransient<IAuthorizationHandler, RecordOwnerHandler>();
services.AddMvc();
}
我尝试使用IHttpContextAccessor
作为RecordOwnerHandler
的构造函数参数,但IHttpContextAccessor.HttpContext
似乎不包含请求的RouteData
。
我进行了几次Google搜索,看看是否有任何关于如何执行此操作的信息,并且显示空白。
然后我挖掘了路由和模型绑定的源代码,但似乎找不到用于注入路由值的抽象进入服务。
我意识到我可以尝试从URL中解析这些信息,但我希望有一种更清晰的方法来获取价值。
那么,如何在ASP.NET Core 2.0中访问服务中的路由值和/或值提供程序数据?
答案 0 :(得分:7)
可以使用ActionContextAccessor
class。
services.AddTransient<IActionContextAccessor, ActionContextAccessor>();
public class RecordOwnerHandler : AuthorizationHandler<RecordOwnerRequirement>
{
private readonly ApplicationDbContext dbContext;
private readonly IActionContextAccessor actionContextAccessor;
public RecordOwnerHandler(ApplicationDbContext dbContext, IActionContextAccessor actionContextAccessor)
{
this.dbContext = dbContext ?? throw new ArgumentNullException(nameof(dbContext));
this.actionContextAccessor = actionContextAccessor ?? throw new ArgumentNullException(nameof(actionContextAccessor));
}
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, RecordOwnerRequirement requirement)
{
if (IsUserAuthorized(context))
{
context.Succeed(requirement);
}
//TODO: Use the following if targeting a version of
//.NET Framework older than 4.6:
// return Task.FromResult(0);
return Task.CompletedTask;
}
private bool IsUserAuthorized(AuthorizationHandlerContext context)
{
// Now the id route value can be accessed directly...
var id = this.actionContextAccessor.ActionContext.RouteData.Values["id"];
// Use the dbContext to compare the id against the database...
// Return the result
return true;
}
}
注意:我仍然想找到一种方法来访问价值提供商来做到这一点,所以如果参数通过路由值,查询字符串传递,那就不重要了形式值等。
答案 1 :(得分:2)
为了将来参考,从 .NET Core 5.0 开始,现在传递 HttpContext,因此您可以执行以下操作:
if (context.Resource is HttpContext httpContext)
{
var value = httpContext.GetRouteValue("key");
}
请参阅此参考[AspNetCore] Authorization resource in endpoint routing will now be HttpContext
答案 2 :(得分:1)
不了解.NET Core 2.0,但是在3.0中,您可以借助IHttpContextAccessor
访问路由值,该服务实现将由框架自动为您提供(因此无需注册任何内容。)
public class RecordOwnerHandler : AuthorizationHandler<RecordOwnerRequirement>
{
private readonly ApplicationDbContext dbContext;
private readonly IHttpContextAccessor httpContextAccessor;
public RecordOwnerHandler(ApplicationDbContext dbContext, IHttpContextAccessor httpContextAccessor)
{
this.dbContext = dbContext ?? throw new ArgumentNullException(nameof(dbContext));
this.httpContextAccessor = httpContextAccessor ?? throw new ArgumentNullException(nameof(httpContextAccessor));
}
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, RecordOwnerRequirement requirement)
{
if (IsUserAuthorized(context))
{
context.Succeed(requirement);
}
//TODO: Use the following if targeting a version of
//.NET Framework older than 4.6:
// return Task.FromResult(0);
return Task.CompletedTask;
}
private bool IsUserAuthorized(AuthorizationHandlerContext context)
{
if (!_httpContextAccessor.HttpContext.Request.RouteValues.TryGetValue("id", out var id))
{
return false;
}
// TODO Whatever logic you need to perform with the id
return true;
}
}
答案 3 :(得分:0)
我有相同的用例,并尝试通过 AuthorizationHandler 中的端点元数据访问路由值,如 ASP.NET Core 5 安全文档中 Access MVC request context in handlers 部分的建议:
AgGridReact
但是 if (!(context.Resource is Endpoint endpoint)) throw new InvalidOperationException("endpoint routing required");
var id = endpoint.Metadata.GetMetadata<ControllerActionDescriptor>()?.RouteValues["id"];
是 context.Resource
类型,所以我不得不这样做:
DefaultHttpContext