Авторизация на основе установленных правил в ASP.NET Core - глубокое погружение

Tags: .NET, ASP.NET Core, Visual Studio

Правильное обеспечение безопасности всегда необходимо, но зачастую не просто. В этой статье мы расскажем об авторизации на основе установленных правил в ASP.NET Core, используемую для создания многократно используемой модели авторизации и упрощения защиты ваших приложений.

Аутентификация и авторизация - это два термина, с которыми вы часто сталкиваетесь, читая о безопасности веб-приложений. В то время как первый используется для проверки учетных данных пользователя, второй используется для предоставления пользователю доступа к одному или нескольким ресурсам приложения. Существует два способа реализации авторизации в ASP.NET Core. К ним относятся авторизация на основе ролей и авторизация на основе установленных правил. Ролевая авторизация использовалась в предыдущих версиях ASP.NET. Авторизация на основе установленных правил была недавно введена в ASP.NET Core и предоставляет богатую, выразительную и многократно используемую модель авторизации для защиты приложений, разработанных в ASP.NET Core. В этой статье обсуждается, как можно работать с авторизацией на основе установленных правил в ASP.NET Core.

Предпосылки

Для работы с примерами кода, приведенными в этой статье,  в вашей системе должны быть установлены Visual Studio 2017 и .NET Core. Если у вас не установлено .NET Core, вы можете скачать его отсюда. Вы можете скачать Visual Studio 2017 по этой ссылке.

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

Авторизация на основе ролей в ASP.NET Core

Роль - это строковое значение, которое сопоставляется с набором разрешений для аутентифицированного пользователя. Модель безопасности на основе ролей используется со времен ASP.NET. Авторизация на основе ролей - это декларативный способ ограничения доступа к ресурсам. Вы можете указать роли, в которых текущий пользователь должен участвовать, чтобы получить доступ к указанному ресурсу. Атрибут Authorize позволяет вам ограничивать доступ к ресурсам на основе ролей. Это декларативный атрибут, который можно применить к контроллеру или методу действия. Если вы укажете этот атрибут без каких-либо аргументов, он только проверяет, аутентифицирован ли пользователь. Вот пример, который иллюстрирует, как этот атрибут может быть применен к контроллеру.

using Microsoft.AspNetCore.Authorization;

using Microsoft.AspNetCore.Mvc;

[Authorize]

public class UserController : Controller

{

 //Action methods

}


Следующий фрагмент кода может использоваться для ограничения доступа к SecurityController только тем пользователям, которые являются администраторами, то есть тем пользователям, которые являются членами роли администратора.

[Authorize(Roles = "Administrator")]

public class SecurityController : Controller

{

 //Usual code

}


Несколько ролей

Вы также можете указать несколько ролей, разделенных запятой. Вот пример, который иллюстрирует это.

[Authorize(Roles = "Manager,Administrator")]

public class DocumentsController : Controller

{

  //Action methods

}

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

Обратите внимание, что вы можете применять роли как на уровне контроллера, так и на уровне действий. Например, даже если пользователи с ролями «Manager» или «Administrator» могут получить доступ к DocumentsController и его методам, вы можете захотеть, чтобы к методу этого класса контроллера обращались только пользователи, принадлежащие к роли Администратор. Следующий фрагмент кода показывает, как этого можно достичь.

[Authorize(Roles = "Manager, Administrator")]

public class DocumentsController : Controller

{

   public ActionResult ViewDocument()

   {

       //Your code here

   }

   [Authorize(Roles = "Administrator")]

   public ActionResult DeleteAllDocuments()

   {

       //Your code here

   }

}

Теперь предположим, что вы хотите, чтобы пользователи, которые принадлежат как к ролям менеджера, так и к администратору, имели доступ только к DocumentsController и его методам. В следующем фрагменте кода показано, как можно определить роли таким образом, чтобы пользователи, которые обращаются к DocumentsController и его методам, должны были быть членами как ролей менеджера, так и администратора.

[Authorize(Roles = "Manager")]

[Authorize(Roles = "Administrator")]

public class DocumentsController  : Controller

{

}

 

Несмотря на простоту использования, авторизация на основе ролей имеет свои ограничения. Именно поэтому используется авторизация на основе установленных правил. В следующих разделах вы узнаете, как работать с авторизацией на основе установленных правил в ASP.NET Core.

Необходимость авторизации на основе установленных правил

Авторизация и контроль доступа с использованием ролей для защиты ресурсов от несанкционированного доступа используются уже довольно давно. Тем не менее, они не очень выразительны, и вы можете столкнуться с проблемами, когда количество необходимых ролей очень велико.

Вот сценарий с примером для объяснения. Предположим, вам поручено разработать структуру безопасности для приложения. Первоначально в приложении есть три роли: пользователь, администратор и менеджер. Теперь, если у вас есть несколько вариантов роли администратора, например CustomerAdmin, ReportsAdmin и SuperAdmin, вам придется учитывать каждый из них при разработке инфраструктуры безопасности. Вы также можете иметь несколько вариантов роли менеджера - ваша структура также должна учитывать их. Поскольку количество этих ролей значительно возрастает, становится чрезвычайно сложно эффективно справляться с этими ролями. Именно здесь и появляется модель авторизации на основе установленных правил.

Работа с авторизацией на основе установленных правил в ASP.NET Core

Модель безопасности на основе установленных правил разъединяет авторизацию и логику приложения и предоставляет гибкую, повторно используемую и расширяемую модель безопасности в ASP.NET Core. Модель безопасности на основе установленных правил основана на трех основных концепциях. К ним относятся политики, требования и обработчики. Политика состоит из нескольких требований. Требование, в свою очередь, содержит параметры данных для проверки личности пользователя. Наконец, обработчик используется для определения, имеет ли пользователь доступ к определенному ресурсу. Мы обсудим каждый из них более подробно в этом разделе - начнем с политики.

 

По сути, политика состоит из одного или нескольких требований и обычно регистрируется при запуске приложения в методе ConfigureServices () файла Startup.cs. Чтобы применить политики в ваших контроллерах или методах действий, вы можете воспользоваться атрибутом AuthorizeAttribute или фильтром AuthorizeFilter.

 

Вы можете создать экземпляр политики, используя класс AuthorizationPolicyBuilder, как показано в приведенном ниже фрагменте кода. Вы можете указать имена ролей, используя метод RequireRole.



var policy = new AuthorizationPolicyBuilder()

 .RequireAuthenticatedUser()

 .RequireRole("Admin")

 .Build();

 

Кроме того, вы можете создать экземпляр  политики в методе ConfigureServices, как показано в приведенном ниже фрагменте кода.

 

services.AddMvc(obj =>

           {

               var policy = new AuthorizationPolicyBuilder()

                   .RequireAuthenticatedUser()

                   .Build();

               obj.Filters.Add(new AuthorizeFilter(policy));

           });

 

Вы увидите больше об этом в следующих разделах.

Регистрация политики

Простого определения установленных правил недостаточно - вы также должны зарегистрировать правила, которые вы определили, с помощью промежуточного программного обеспечения авторизации. Чтобы зарегистрировать политику вы должны указать имя - это имя будет использоваться для ссылки на нее в контроллере или методы действия.

В следующем фрагменте кода показано, как можно зарегистрировать политику при запуске приложения в ASP.NET Core.

using Microsoft.Extensions.DependencyInjection;

public void ConfigureServices(IServiceCollection services)

{

   services.AddMvc();

   services.AddAuthorization(options =>

   {

       options.AddPolicy("RequireManagerOnly", policy =>

           policy.RequireRole("Manager"));

   });

}

 

Вы также можете указать несколько разрешенных ролей при регистрации политики, как показано в фрагменте кода ниже.

 

  services.AddAuthorization(options =>

   {

       options.AddPolicy("RequireManagerOnly", policy =>

             policy.RequireRole("Manager","Administrator"));

   });

 

Применение политики

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

 

[Authorize(Policy = "RequireAdminOnly")]

public class SecurityController: Controller

{

   //Action methods

}

Как видите, вместо указания ролей в атрибуте [Authorize] вы можете указать политику, которую вы хотите применить. Чтобы применить политику к методу действия, вы можете воспользоваться свойством Policy атрибута Authorize, как показано в фрагменте кода ниже.

 

[Authorize(Policy = "RequireAdminOnly")]

public IActionResult DeleteAllSecureDocuments()

{

   //Some code

}

Вы также можете применить несколько политик к контроллеру или методу действия. Следующий фрагмент кода иллюстрирует, как этого можно достичь.

 

[Authorize(Policy = "ShouldBeEmployeeOnly")]

public class SecurityController : Controller

{

   [Authorize(Policy = "RequireAdminOnly")]

   public ActionResult DeleteUser()

   {

       //Your code here

   }

}

В приведенном выше примере кода вы видите две примененные политики - одну на уровне контроллера, а другую на уровне действий. Обратитесь к методу действия DeleteUser в приведенном выше примере кода. Этот метод действия может быть выполнен только теми пользователями, которые удовлетворяют обеим политикам, а именно ShouldBeEmployeeOnly и Administrator. Другими словами, чтобы вызвать метод DeleteUser, удостоверение должно соответствовать двум политикам ShouldBeEmployeeOnly и Administrator.

Хотя авторизацию на основе ролей легко реализовать в ASP.NET Core, она имеет ограниченную область действия. В качестве примера представьте, что вам нужно проверить пользователя на основе даты присоединения или идентификатора отдела. Вы не можете иметь роли для каждого из таких вариантов - это совсем не хорошее решение. Здесь вы можете воспользоваться преимуществами авторизации на основе утверждений - вы можете проверить личность пользователя на основе утверждений. В следующем разделе рассматривается, как можно работать с авторизацией на основе утверждений с помощью политик.

Использование авторизации на основе утверждений через политики

Авторизация на основе утверждений обеспечивает декларативный способ проверки доступа к ресурсам. При таком типе авторизации вы обычно проверяете значение утверждения, а затем предоставляете доступ к ресурсу на основе значения, содержащегося в утверждении. Прежде всего, нужно понять, что такое утверждение. Утверждение - это пара ключ-значение, представляющая субъекта, т. е. имя, возраст, номер паспорта, водительское удостоверение, паспорт, национальность, дата рождения и т. д. Таким образом, если dateofbirth является именем утверждения, значением утверждения будет дата рождения, т.е. 1 января 1970 г.

public void ConfigureServices(IServiceCollection services)

{

   services.AddMvc();

   services.AddAuthorization(options =>

   {

       options.AddPolicy("ShouldBeOnlyEmployee", policy =>

             policy.RequireClaim("EmployeeId"));

   });

}

Затем вы можете применить эту политику на уровне контроллера к атрибуту AuthorizeAttribute, как показано ниже.

[Authorize(Policy = "ShouldBeOnlyEmployee")]

public IActionResult SomeMethod()

{

   //Write your code here

}


Вы также можете иметь политики с несколькими утверждениями. Вы должны соответствующим образом зарегистрировать их в методе ConfigureServices класса Startup, как показано в приведенном ниже фрагменте кода.

public void ConfigureServices(IServiceCollection services)  

{  

   services.AddMvc().SetCompatibilityVersion(

         CompatibilityVersion.Version_2_1);  

   services.AddAuthorization(options =>  

   {

       options.AddPolicy("CustomSecurityPolicy", policy =>

         policy.RequireClaim("ShouldBeOnlyEmployee"));  

       options.AddPolicy("CustomSecurityPolicy", policy =>

         policy.RequireClaim("IsAdmin", "true"));  

   });

}  

Требования

Под покровом используются методы авторизации на основе ролей и утверждений, основанные на требованиях, обработчике и политике. В этом и последующих разделах будет рассмотрен каждый из них.

Требование включает в себя набор параметров данных. Эти параметры данных используются политикой для оценки личности пользователя. Чтобы создать требование, вам нужно создать класс, который реализует интерфейс IAuthorizationRequirement. Следующий фрагмент кода иллюстрирует требование для политики MinimumExp - вы зарегистрируете эту политику через некоторое время.

public class MinimumExpRequirement : IAuthorizationRequirement

{

   public int MinimumExp { get; set; }

   public MinimumExpRequirement(int experience)

   {

       MinimumExp = experience;

   }

}

Обработчики авторизации

Требование может иметь один или несколько обработчиков. Обработчик авторизации используется для оценки свойств требования. Чтобы создать обработчик авторизации, вы должны создать класс, который расширяет AuthorizationHandler <T> и реализует метод HandleRequirementAsync (). Следующий фрагмент кода показывает, как выглядит типичный обработчик авторизации.

public class MinimumExpHandler :

AuthorizationHandler<MinimumExpRequirement>

   {

       protected override Task HandleRequirementAsync(

           AuthorizationHandlerContext context,

           MinimumExpRequirement requirement)

       {

           throw new NotImplementedException();

       }

   }

В следующем фрагменте кода показано, как можно написать необходимую логику авторизации в методе HandleRequirementsAsync, чтобы найти утверждение и оценить требование.

public class MinimumExpHandler :         

 AuthorizationHandler<MinimumExpRequirement>

   {

       protected override Task HandleRequirementAsync(

           AuthorizationHandlerContext context,

           MinimumExpRequirement requirement)

       {

           var user = context.User;

           var claim = context.User.FindFirst("MinExperience");

           if(claim != null)

           {

               var expInYears = int.Parse(claim?.Value);

               if (expInYears >= requirement.MinimumExp)

                   context.Succeed(requirement);

           }

           return Task.CompletedTask;

       }

   }

 

Теперь, как узнать, успешно ли выполнен обработчик, то есть что должен вернуть обработчик? Хорошо, если требование было успешно оценено, вы можете вызвать метод Succeed в экземпляре AuthorizationHandlerContext и передать экземпляр требования в качестве параметра методу. В приведенном выше фрагменте кода обратите внимание на то, как был вызван метод Succeed.

Несколько обработчиков для одного требования

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

 

public class EmployeeRequirement : IAuthorizationRequirement

{

  //Write your code here

}

public class EmployeeRoleHandler :

     AuthorizationHandler<EmployeeRequirement>

{

   //Write your code here to check if the user is an employee

}

public class MinimumAgeHandler :

     AuthorizationHandler<EmployeeRequirement>

{

   //Write your code here to validate min age

}


Регистрация обработчика

Вы должны зарегистрировать обработчиков в коллекции сервисов. Чтобы зарегистрировать обработчик, созданный ранее в этой статье, необходимо написать следующий код в методе ConfigureServices класса Startup, как показано в фрагменте кода ниже.

public void ConfigureServices(IServiceCollection services)

       {

           //Other code

           services.AddSingleton<IAuthorizationHandler,

           MinimumExpHandler>();

       }

Обратите внимание на использование метода AddSingleton в методе ConfigureServices, приведенном выше. При работе с внедрением зависимостей в ASP.NET Core вы можете указать время жизни службы, используя методы AddTransient, AddScoped или AddSingleton. В этом примере используется время жизни синглтона. При использовании этого типа срока службы службы экземпляр службы будет создан при первом запросе. Последующие запросы к службе будут повторно использовать тот же экземпляр.

Вот полный список кода метода ConfigureServices - и политика, и обработчик зарегистрированы в конвейере.

public void ConfigureServices(IServiceCollection services)

       { services.AddMvc().SetCompatibilityVersion(

           CompatibilityVersion.Version_2_1);

           services.AddAuthorization(options =>

           {

               options.AddPolicy(

                   "MinExperience", policy =>

                   policy.Requirements.Add(

                         new MinimumExpRequirement(5)));

           });

           services.AddSingleton<IAuthorizationHandler,

               MinimumExpHandler>();

       }

Резюме

Модель авторизации в ASP.NET Core была значительно обновлена с введением простой декларативной модели авторизации на основе установленных правил. Авторизация на основе установленных правил является гибкой и помогает вам построить свободно связанную модель безопасности путем отделения авторизации и логики приложения. Кстати, ASP.NET Core поддерживает авторизацию на основе ролей и установленных правил. В этой статье рассматриваются модель авторизации на основе установленных правил, ее преимущества и способы работы с ней в ASP.NET Core.

No Comments

Add a Comment