Отредактировано на основе комментариев
Насколько я понимаю, вы хотите получить доступ к текущему пользователю (всю информацию, связанную с ним), роли (ролям), которые вы хотите указать для контроллера (или действия), и параметрам, полученным конечной точкой. Не пробовал для веб-API, но для ядра MVC asp.net. Вы можете достичь этого, используя AuthorizationHandler
в авторизации на основе политик, и в сочетании с внедренным сервисом, специально созданным для определения доступа к ресурсам ролей.
Для этого сначала настройте политику в Startup.ConfigureServices
:
services.AddAuthorization(options =>
{
options.AddPolicy("UserResource", policy => policy.Requirements.Add( new UserResourceRequirement() ));
});
services.AddScoped<IAuthorizationHandler, UserResourceHandler>();
services.AddScoped<IRoleResourceService, RoleResourceService>();
затем создайте UserResourceHandler
:
public class UserResourceHandler : AuthorizationHandler<UserResourceRequirement>
{
readonly IRoleResourceService _roleResourceService;
public UserResourceHandler (IRoleResourceService r)
{
_roleResourceService = r;
}
protected override async Task HandleRequirementAsync(AuthorizationHandlerContext authHandlerContext, UserResourceRequirement 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 (_roleResourceService.IsAuthorize(area, controller, action, id))
{
context.Succeed(requirement);
}
}
}
}
Доступ к параметрам, полученным конечной точкой, достигается путем приведения context.Resource
к AuthorizationFilterContext
, чтобы мы могли получить к нему доступ RouteData
. Что касается UserResourceRequirement
, мы можем оставить его пустым.
public class UserResourceRequirement : IAuthorizationRequirement { }
Что касается IRoleResourceService
, это простой класс обслуживания, поэтому мы можем внедрить в него все, что угодно. Этот сервис является заменой сопряжения Роли с действием в коде, поэтому нам не нужно указывать его в атрибуте действия. Таким образом, у нас может быть свобода выбора реализации, например: из базы данных, из файла конфигурации или в жестком коде.
Доступ к пользователю в RoleResourceService
достигается путем введения IHttpContextAccessor
. Обратите внимание: чтобы сделать IHttpContextAccessor
инъекционным, добавьте services.AddHttpContextAccessor()
в Startup.ConfigurationServices
теле метода.
Вот пример получения информации из файла конфигурации:
public class RoleResourceService : IRoleResourceService
{
readonly IConfiguration _config;
readonly IHttpContextAccessor _accessor;
readonly UserManager<AppUser> _userManager;
public class RoleResourceService(IConfiguration c, IHttpContextAccessor a, UserManager<AppUser> u)
{
_config = c;
_accessor = a;
_userManager = u;
}
public bool IsAuthorize(string area, string controller, string action, string id)
{
var roleConfig = _config.GetValue<string>($"RoleSetting:{area}:{controller}:{action}"); //assuming we have the setting in appsettings.json
var appUser = await _userManager.GetUserAsync(_accessor.HttpContext.User);
var userRoles = await _userManager.GetRolesAsync(appUser);
// all of needed data are available now, do the logic of authorization
return result;
}
}
Конечно, получить настройку из базы данных немного сложнее, но это можно сделать, поскольку мы можем ввести AppDbContext
. Для жестко закодированного подхода существует множество способов сделать это.
После того, как все сделано, используйте политику для действия:
[Authorize(Policy = "UserResource")] //dont need Role name because of the RoleResourceService
public ActionResult<IActionResult> GetSomething(int resourceId)
{
//existing code
}
Фактически, мы можем использовать политику «UserResource» для любых действий, которые мы хотим применить.