Безграмотный policy asp

Along with role-based and claim based authorization, ASP.NET Core also supports the policy-based authorization. A policy is nothing but a collection of requirements with different data parameters to evaluate the user Identity. Read more about the policy-based authorization . This short post shows how to implement single authorization policy in ASP.NET Core 2.0 application. Once implemented, the policy becomes global and applicable to the whole application.

To implement single authorization policy, open Startup.cs and add the following code in the ConfigureServices method to enable authorization for all the controllers (be it MVC or API).

Public void ConfigureServices(IServiceCollection services) { services.AddMvc(o => { var policy = new AuthorizationPolicyBuilder() .RequireAuthenticatedUser() .Build(); o.Filters.Add(new AuthorizeFilter(policy)); }); }

This code ensures that every page will require authentication as the policy will demand the user to be authenticated. Only the actions marked with can be accessed.

The above code block will enable authorization for all the environments (Development, staging or production). However, you don’t want the same behavior in the development environment as it can increase the testing efforts. It would be appropriate to exclude the development environment. To exclude development environment, we need to check for the environment and write code accordingly. To do that, let’s modify the ConfigureServices method to take IHostingEnvironment service and check for the development environment. Like:

Public void ConfigureServices(IServiceCollection services, IHostingEnvironment env) { if (!env.IsDevelopment()) { services.AddMvc(o =>

Save the changes and run the application. And you should be surprised to see the following exception message in the browser.

“The ConfigureServices method must either be parameterless or take only one parameter of type IServiceCollection.”

The message clearly says, the ConfigureServices method either should be with no parameters or with only one parameter. Therefore, we can’t directly inject the IHostingEnvironment in the ConfigureServices method. Then, the question is how do we make it available in the ConfigureServices method?

Well, we can inject the IHostingEnvironment service in the Startup class constructor and store it in a variable. The existing Startup class constructor creates the IConfigurationRoot using a ConfigurationBuilder and saves it to a property on Startup called Configuration . We can take the same approach for IHostingEnvironment by saving it to a property on Startup, for use later in ConfigureServices . The Startup class constructor would look something like this:

Public Startup(IConfiguration configuration, IHostingEnvironment env) { Configuration = configuration; HostingEnvironment = env; } public IConfiguration Configuration { get; } public IHostingEnvironment HostingEnvironment { get; }

Next, we can use the HostingEnvironment variable in ConfigureServices method to check for the environment. We’ll enable the authorization only for environments other than development.

Public void ConfigureServices(IServiceCollection services) { if (!HostingEnvironment.IsDevelopment()) { services.AddMvc(o => { var policy = new AuthorizationPolicyBuilder() .RequireAuthenticatedUser() .Build(); o.Filters.Add(new AuthorizeFilter(policy)); }); } else services.AddMvc(); }

Bonus Tip: If you are already using either JWT or OAuth authentication for your application and wish to disable authentication in the development environment, then use the following code in ConfigureServices method.

If (HostingEnvironment.IsDevelopment()) { services.AddMvc(opts => { opts.Filters.Add(new AllowAnonymousFilter()); }); } else { services.AddMvc(); }

To summarize, the technique shown above helps you to implement single authorization policy globally in ASP.NET Core 2.0 apps. You can easily enable it only for the staging or production environment.

Thank you for reading. Keep visiting this blog and share this in your network. Please put your thoughts and feedback in the comments section.

Heads up… this article is old!

Role-Based Authorization in ASP.NET Core

If you’re familiar with roles in ASP.NET 4.x, you’ll find that the new features start from a familiar place. Specifically, a user can have several roles and you define what roles are required to perform a certain action, or access to specific sections or resources, within your application. You can specify what roles are authorized to access to a specific resource by using the attribute. It can be declared in such a way that the authorization could be evaluated at controller level, action level, or even at a global level.

Authorization in ASP.NET Core with Stormpath

Now, let’s look at how easy is to use Stormpath with the policy-based approach. The Stormpath ASP.NET Core library provides two ways to easily enforce authorization in your application: group- (or role-) based access control, and permissions-based access control.

To easily configure Stormpath in your ASP.NET Core project check out the .

Use Stormpath Groups to Model Authorization Roles

If you need to organize your users by Role or Group, Stormpath has role-based access control built in. User accounts can belong to one or many groups, each of which can carry its own set of permissions.

With Stormpath, you can create nested or hierarchical groups, model organizational functions, or implement best-practice resource-based access control.

Custom data is not only useful to store additional info to the user accounts of your application; it also can be used to combine user claims with policies to implement fine-grained authorization.

And that’s all! As you can see, using custom data combined with policy-based authorization is super easy thanks to the Stormpath ASP.NET Core library . With a few line of code you have added a new policy that handles authorization based on the user custom data.

Learn More About User Management, Including Authentication and Authorization, in ASP.NET Core

With ASP.NET Core and Stormpath you can model your security with a considerable number of benefits. Policy-Based Authorization allows you to write more flexible, reusable, self-documented, unit-testable, and encapsulated code. Stormpath is ready to work with this approach in a super clean and elegant way.

To learn more about Stormpath and ASP.NET Core check out these resources.

Последнее обновление: 06.09.2017

Создадим новый проект ASP.NET Core 2.0 по типу WebApplication (Model-View-Controller), который назовем ClaimsApp.

Затем добавим в проект новую папку для моделей, которую назовем Models. И определим в этой папке класс пользователя:

Public class User { public int Id { get; set; } public string Email { get; set; } public string Password { get; set; } public string City { get; set; } public string Company { get; set; } public int Year { get; set; } }

И также добавим в папку Models новый класс ApplicationContext, который будет представлять контекст данных:

Using Microsoft.EntityFrameworkCore; namespace ClaimsApp.Models { public class ApplicationContext: DbContext { public DbSet Users { get; set; } public ApplicationContext(DbContextOptions options) : base(options) { } } }

Теперь изменим класс Startup для установки и использования контекста данных:

Using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using ClaimsApp.Models; using Microsoft.EntityFrameworkCore; using System.Security.Claims; using Microsoft.AspNetCore.Authentication.Cookies; namespace ClaimsApp { public class Startup { public Startup(IConfiguration configuration) { Configuration = configuration; } public IConfiguration Configuration { get; } public void ConfigureServices(IServiceCollection services) { string connection = "Server=(localdb)\\mssqllocaldb;Database=claimsstoredb;Trusted_Connection=True;"; services.AddDbContext(options => options.UseSqlServer(connection)); services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme) .AddCookie(options => { options.LoginPath = new Microsoft.AspNetCore.Http.PathString("/Account/Register"); }); services.AddAuthorization(opts => { opts.AddPolicy("OnlyForLondon", policy => { policy.RequireClaim(ClaimTypes.Locality, "Лондон", "London"); }); opts.AddPolicy("OnlyForMicrosoft", policy => { policy.RequireClaim("company", "Microsoft"); }); }); services.AddMvc(); } public void Configure(IApplicationBuilder app) { app.UseStaticFiles(); app.UseAuthentication(); app.UseMvc(routes => { routes.MapRoute(name: "default", template: "{controller=Home}/{action=Index}/{id?}"); }); } } }

Здесь устанавливаются две политики - "OnlyForLondon" и "OnlyForMicrosoft". Первая политика требует, чтобы claim с типом ClaimTypes.Locality имел значение "London" или "Лондон". Если значений много, то мы их можем перечислить через запятую. Вторая политика требует наличия Claim с типом "company" и значением "Microsoft".

Для регистрации определим в папке Models дополнительную модель RegisterModel:

Using System.ComponentModel.DataAnnotations; namespace ClaimsApp.Models { public class RegisterModel { public string Email { get; set; } public string Password { get; set; } public string ConfirmPassword { get; set; } public string City { get; set; } public string Company { get; set; } public int Year { get; set; } } }

И также для представлений контроллера добавим в папку Views подкаталог Account и поместим в него новое представление Register.cshtml :

@model ClaimsApp.Models.RegisterModel

Регистрация







А в контроллере AccountController определим действие регистрации:

Using System.Collections.Generic; using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; using ClaimsApp.Models; using Microsoft.EntityFrameworkCore; using System.Security.Claims; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authentication.Cookies; namespace ClaimsApp.Controllers { public class AccountController: Controller { private ApplicationContext _context; public AccountController(ApplicationContext context) { _context = context; } public IActionResult Register() { return View(); } public async Task Register(RegisterModel model) { if (ModelState.IsValid) { User user = await _context.Users.FirstOrDefaultAsync(u => u.Email == model.Email); if (user == null) { // добавляем пользователя в бд user = new User { Email = model.Email, Password = model.Password, Year = model.Year, City = model.City, Company = model.Company }; _context.Users.Add(user); await _context.SaveChangesAsync(); await Authenticate(user); return RedirectToAction("Index", "Home"); } else ModelState.AddModelError("", "Некорректные логин и(или) пароль"); } return View(model); } private async Task Authenticate(User user) { // создаем один claim var claims = new List { new Claim(ClaimsIdentity.DefaultNameClaimType, user.Email), new Claim(ClaimTypes.Locality, user.City), new Claim("company", user.Company) }; // создаем объект ClaimsIdentity ClaimsIdentity id = new ClaimsIdentity(claims, "ApplicationCookie", ClaimsIdentity.DefaultNameClaimType, ClaimsIdentity.DefaultRoleClaimType); // установка аутентификационных куки await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, new ClaimsPrincipal(id)); } } }

В итоге весь проект будет выглядеть следующим образом:

Для тестирования доступа изменим контроллер HomeController:

Public class HomeController: Controller { public IActionResult Index() { return View(); } public IActionResult About() { ViewData["Message"] = "Your application description page."; return View(); } }

Здесь метод Index доступен только для тех пользователей, которые удовлетворяют политике "OnlyForLondon", а метод About - для пользователей, соответствующих политике "OnlyForMicrosoft".

И пусть в представлении для метода Index выводятся все объекты Claim для текущего пользователя:

@using System.Security.Claims @foreach(var claim in User.Claims.ToList()) {

@claim.Type: @claim.Value

}
Город: @User.FindFirst(x => x.Type == ClaimTypes.Locality).Value
Компания: @User.FindFirst(x => x.Type == "company").Value

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

И после регистрации перейдем к методу Index контроллера HomeController.