Net Core 3 Роль пользователя в зависимости от арендатора - PullRequest
0 голосов
/ 09 февраля 2020

У меня есть вопрос об управлении пользователями и ролями в службах REST, разработанных с помощью Net Core 3 Я изучаю, как работает управление пользователями и ролями в ASP NET Core 3, но у меня есть такое сомнение: обычно пользователь связан с ролью.

В методах контроллера вы можете указать, для каких ролей будет доступен метод. Но если роль моего пользователя меняется в зависимости от клиента, на которого он работает, как я могу управлять им?

Позвольте мне объяснить: пользователь может получить доступ к приложению, а затем выбрать, на какого клиента работать. В зависимости от выбранного клиента, он может иметь разные роли: администратор для клиента A, Collaborator для клиента B и т. Д. c.

Таким образом, роль связана не только с пользователем, но и с клиентом, для которого он работает. Можно ли управлять этим типом авторизации с помощью Net Core 3 Identities?

1 Ответ

2 голосов
/ 10 февраля 2020

"Таким образом, роль связана не только с пользователем, но и с клиентом, для которого он работает" * :

Во-первых, вам нужна таблица который хранит отношения Оператор-Роль-Клиент. Вы можете расширить встроенную таблицу AspNetUserRoles или создать новую таблицу. Поскольку мы не знаем, как выглядит ваше приложение, я создам новую таблицу, чтобы проиллюстрировать, как это сделать. Допустим, у нас есть два оператора:

  • op1 является одновременно администратором и соавтором для клиента 1,
  • , а op2 является соавтором для клиента 2:
 operatorId    |    roleId      |    customerId
---------------+----------------+-----------------
  op1          | Administrator  |        1
---------------+----------------+-----------------
  op1          | Collaborator   |        2
---------------+----------------+-----------------
  op2          | Collaborator   |        2

и затем вместо непосредственного использования [Authorize(Roles="xxx")] мы можем определить политику CheckOperatorRoleForCustomer , которая динамически проверяет отношения оператор-роль-клиент:

services.AddAuthorization(opts =>{
    opts.AddPolicy("CheckAdminForCustomer", pb =>{
        pb.RequireAuthenticatedUser().AddRequirements(new OpRoleForCustomerRequirement("Administrator"));
    });
    opts.AddPolicy("CheckCollaboratorForCustomer", pb =>{
        pb.RequireAuthenticatedUser().AddRequirements(new OpRoleForCustomerRequirement("Collaborator"));
    });
});
services.AddHttpContextAccessor();
services.AddScoped<IAuthorizationHandler, OpRoleForCustomerHandler>();

Здесь OpRoleForCustomerHandler является AuthorizationHandler , который проверяет, имеет ли текущий пользователь требуемую роль для указанного клиента.

Для удобства я определяю здесь две политики:

  • CheckAdminForCustomer : проверить, имеет ли текущий пользователь роль Администратор для указанного клиента
  • CheckCollaboratorForCustomer : проверить, имеет ли текущий пользователь роль Collaborator для указанного клиента

И затем вы можете применить политику:

[Authorize(Policy="CheckAdminForCustomer")]
public IActionResult Profile(int CustomerId)
{

This это так называемая авторизация на основе политик , которая более мощная, чем авторизация на основе ролей.

Наконец, вот моя реализация OpRoleForCustomerHandler для справки:

public class OpRoleForCustomerRequirement: IAuthorizationRequirement
{
    public OpRoleForCustomerRequirement(string RoleId)
    {
        this.RoleId = RoleId;
    }
    public string RoleId{get;set;}
}
public class OpRoleForCustomerHandler: AuthorizationHandler<OpRoleForCustomerRequirement> 
{
    private readonly AppIdentityDbContext _dbContext;
    private readonly UserManager<IdentityUser> _userManager;
    private readonly IHttpContextAccessor _httpAccessor;

    public OpRoleForCustomerHandler(AppIdentityDbContext dbContext, UserManager<IdentityUser> userManager, IHttpContextAccessor accessor) 
    {
        this._dbContext = dbContext;
        this._userManager = userManager;
        this._httpAccessor = accessor;
    }

    protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, OpRoleForCustomerRequirement requirement)
    {
        if(context.User==null) { /* ...log... */ context.Fail(); return; }
        var user = await this._userManager.GetUserAsync(context.User);

        var httpContext = this._httpAccessor.HttpContext;
        // get customId from HttpContext/RouteData/....
        // for example ....
        var customIdStr = httpContext.Request.Query["customerId"].FirstOrDefault(); 

        if (!string.IsNullOrEmpty(customIdStr) ) { 
            var matches = this._dbContext.OpRoleForCustomers
                .Any(opc => 
                    opc.OperatorId == user.Id 
                    && opc.RoleId == requirement.RoleId 
                    && opc.CustmerId == customIdStr
                );
            if(matches){ context.Succeed(requirement) ; return; }
        }
        context.Fail();
    }
}

(Возможно, вы захотите настроить способ получения customId из HttpContext / RouteData /...)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...