Authorisation Policy in Identity 3 MVC 6

落花浮王杯 提交于 2019-12-05 18:40:26

In this case you'd use resource based, with the account being the resource. The documentation for this is at https://docs.asp.net/en/latest/security/authorization/resourcebased.html

To start with you'd define an operation of Read,

public static class Operations
{
    public static OperationAuthorizationRequirement Read =
        new OperationAuthorizationRequirement   { Name = "Read" };
}

Now you'd have a policy for AccountAccess

public class AccountAuthorizationHandler : AuthorizationHandler<
    OperationAuthorizationRequirement, Account>
{
    IUserManager _userManager;

    public AccountAuthorizationHandler(IUserManager userManager)
    {
        _userManager = userManager;
    }

    protected override void Handle(AuthorizationContext context,
                                   OperationAuthorizationRequirement requirement,
                                   Account resource)
    {
        // Pull the user ID claim out from the context.User
        var userId = context.User.....
        // Get the current user's account numbers.       
        var user = userManager.Users
            .Include(u => u.AuthorisedAccounts)
            .Where(u => u.Id == userId)
            .FirstOrDefault();
    }

    // Now check if the user's account numbers match the resource accountNumber, and 
    // also check the operation type, in case you want to vary based on create, view etc.
    if (user.AuthorisedAccounts.Contains(resource.AccountId &&
        requirement.Name == "View")
   {
      context.Succeed(requirement);
   } 
}

After that register your policy in the DI container, within configure services;

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc();

    services.AddAuthorization();

    services.AddSingleton<IAuthorizationHandler,
                          AccountAuthorizationHandler>();
}

In your controller you inject the AuthorizationService;

public class AccountController : Controller
{
    IAuthorizationService _authorizationService;

    public AccountController(IAuthorizationService authorizationService)
    {
        _authorizationService = authorizationService;
    }
}

Then, within your controller, after you've loaded the account resource you'd do something like

public async Task<IActionResult> View(int accountId)
{
    Account account = accountManager.Find(accountId);

    if (account == null)
    {
        return new HttpNotFoundResult();
    }

    if (await _authorizationService.AuthorizeAsync(User, account, Operations.Read))
    {
        return View(account);
    }
    else
    {
        return new ChallengeResult();
    }
}

If you wanted to define your authorization policy in a way that lets you use it for an [Authorize] attribute, you could alter the approach seen in this article:

services.AddAuthorization(options =>
{
    // inline policies
    options.AddPolicy("AccessByAccountNumber", policy =>
    {
        policy.RequireDelegate((context, requirement) =>
        {
            var httpContext = (context as dynamic).HttpContext;

            // Proceed to grab the account number from the request values
            // and compare it against the user object stored in 'context.User'
        });
    });
});

The downside of this is that you would need to ensure that all of the actions where you use the attribute do so in a consistent way, i.e. using the same name for the action parameter or route parameter every time.

@blowdart's example of using the IAuthorizationService in the controller action demonstrates a way of using and re-using a policy which allows for the parameter names to be decoupled from the policy itself. Though we can see that the IAuthorizationService does not offer a strongly-typed generic method, it would seem to offer a less brittle approach to implementing resource-based policies.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!