2015-12-15 5 views
3

Я провел много исследований, но до сих пор не уверен, правильно ли я делаю это. Лучшего ресурса я нашел здесьПолитика авторизации в Identity 3 MVC 6

http://leastprivilege.com/2015/10/12/the-state-of-security-in-asp-net-5-and-mvc-6-authorization/

Учитывая класс ApplicationUser расширен, чтобы включить в список разрешенных номеров счетов, я хочу, чтобы ограничить пользователь только просматривать выписки (и другие действия, основанных) на свои уполномоченные счетах). Я бы подумал, что это очень распространенный дизайн, однако большинство статей в сети относятся к предыдущим версиям личности.

(PS Я инъекционный UserManager в конструкторе контроллера)

Вот мое действие

public IActionResult GetStatement(int accountNo,DateTime startDate,DateTime endDate) 
{ 
    var user = userManager.Users 
     .Include(u => u.AuthorisedAccounts) 
     .Where(u => u.Id == User.GetUserId()) 
     .FirstOrDefault(); 
    if (user.AuthorisedAccounts != null) 
    { 
     foreach (var account in user.AuthorisedAccounts) 
     { 
      if (account.AccountNo == accountNo) 
       return View(statementService.GetStatement(accountNo, startDate, endDate, 0)); 
     } 
    } 
    return HttpUnauthorized(); 
} 

Я не могу избавиться от ощущения, что есть лучший способ? В основном я хочу авторизовать на основе параметра действия. «AccountNo»

Любые подсказки о том, какой подход принять.

+0

Одна вещь, которую следует отметить - разрешение не является частью личности. Это совершенно отдельный :) – blowdart

ответ

3

В этом случае вы будете использовать ресурс, с учетной записью, являющейся ресурсом. Документация для этого в https://docs.asp.net/en/latest/security/authorization/resourcebased.html

Для начала вы бы определить операцию Read,

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

Теперь вы бы политику для 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); 
    } 
} 

После этого зарегистрировать ваш политика в контейнере DI в службах конфигурации;

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

    services.AddAuthorization(); 

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

В вашем контроллере вы вводите AuthorizationService;

public class AccountController : Controller 
{ 
    IAuthorizationService _authorizationService; 

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

Затем в контроллере, после того, как вы загрузили ресурс учетной записи вы могли бы сделать что-то вроде

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(); 
    } 
} 
+0

OK Реализация этого сегодня. Я довольно новичок в DI, но в этом случае у меня проблема с «сервисами».AddInstance ( новый Authorization.AccountAuthorizationHandler()); 'кажется, я не могу создать экземпляр этого файла без передачи usermanager. Но я думал, что обработчик авторизации отсортировал свой собственный DI? –

+0

Я идиот. Обновлен регистрационный код. – blowdart

+0

Является ли Singleton безопасным с UserManager, поскольку он использует DbContext? –

0

Если вы хотите, чтобы определить свою политику авторизации таким образом, что позволяет использовать его для атрибута [Authorize], вы могли бы изменить подход видели 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' 
     }); 
    }); 
}); 

недостатком этого является то, что вы должны убедиться, что все ас где вы используете атрибут, делаете это последовательно, т. е. каждый раз используйте одно и то же имя для параметра действия или параметра маршрута.

@ Пример использования ddddart использования IAuthorizationService в действии контроллера демонстрирует способ использования и повторного использования политики, которая позволяет отделить имена параметров от самой политики. Хотя мы видим, что IAuthorizationService не предлагает строго типизированный общий метод, он, как представляется, предлагает менее хрупкий подход к реализации политик на основе ресурсов.

+0

Это выглядит интересно, будет ли атрибут [Authorize («AccessByAccountNumber»)] Спасибо большое BTW –

+0

Это было бы правильно. – tuespetre

+0

Проблема с разбором значений запроса обычно не такая полезная. Сам ресурс будет содержать информацию для принятия решения, например, документ имеет поле автора, поэтому мы не рекомендуем этот маршрут. Он работает в этом случае, потому что пользовательский объект содержит список номеров учетных записей, а не учетную запись, содержащую список авторизованных пользователей, что является более типичным. – blowdart