Пример авторизации на основе Claims

Данное руководство устарело. Актуальное руководство: Руководство по ASP.NET Core 7

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

Создадим новый проект ASP.NET Core по типу 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; }
}

Для взаимодействия с MS SQL Server через Entity Framework добавим в проект через Nuget пакет Microsoft.EntityFrameworkCore.SqlServer. И также добавим в папку Models новый класс ApplicationContext, который будет представлять контекст данных:

using Microsoft.EntityFrameworkCore;

namespace ClaimsApp.Models
{
    public class ApplicationContext : DbContext
    {
        public DbSet<User> Users { get; set; }
        public ApplicationContext(DbContextOptions<ApplicationContext> options)
            : base(options)
        {
			Database.EnsureCreated();
        }
    }
}

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

using System.Security.Claims;
using ClaimsApp.Models;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Builder;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;

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<ApplicationContext>(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.AddControllersWithViews();
        }

        public void Configure(IApplicationBuilder app)
        {
            app.UseDeveloperExceptionPage();

            app.UseStaticFiles();

            app.UseRouting();

            app.UseAuthentication();
            app.UseAuthorization();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllerRoute(
                    name: "default",
                    pattern: "{controller=Home}/{action=Index}/{id?}");
            });
        }
    }
}

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

Для тестирования авторизации добавим в папку Controllers новый контроллер AccountController, который будет выполнять регистрацию пользователей.

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

using System.ComponentModel.DataAnnotations;

namespace ClaimsApp.Models
{
    public class RegisterModel
    {
        [Required(ErrorMessage = "Не указан Email")]
        public string Email { get; set; }

        [Required(ErrorMessage = "Не указан пароль")]
        [DataType(DataType.Password)]
        public string Password { get; set; }

        [DataType(DataType.Password)]
        [Compare("Password", ErrorMessage = "Пароль введен неверно")]
        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

<h2>Регистрация</h2>

<form asp-action="Register" asp-controller="Account" asp-anti-forgery="true">
    <div class="validation" asp-validation-summary="All"></div>
    <div>
        <div>
            <label asp-for="Email">Введите Email</label><br />
            <input type="text" asp-for="Email" />
        </div>
        <div>
            <label asp-for="Password">Введите пароль</label><br />
            <input asp-for="Password" />
        </div>
        <div>
            <label asp-for="ConfirmPassword">Повторите пароль</label><br />
            <input asp-for="ConfirmPassword" />
        </div>
        <div>
            <label asp-for="City">Введите город</label><br />
            <input type="text" asp-for="City" />
        </div>
        <div>
            <label asp-for="Company">Введите компанию</label><br />
            <input type="text" asp-for="Company" />
        </div>
        <div>
            <label asp-for="Year">Введите год рождения</label><br />
            <input type="text" asp-for="Year" />
        </div>
        <div>
            <input type="submit" value="Регистрация" />
        </div>
    </div>
</form>

А в контроллере 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;
        }

        [HttpGet]
        public IActionResult Register()
        {
            return View();
        }
        [HttpPost]
        [ValidateAntiForgeryToken]
        public async Task<IActionResult> 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<Claim>
            {
                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));
        }
    }
}

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

Авторизация claims в ASP.NET Core

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

public class HomeController : Controller
{
    [Authorize(Policy ="OnlyForLondon")]
    public IActionResult Index()
    {
        return View();
    }
    
	[Authorize(Policy = "OnlyForMicrosoft")]
    public IActionResult About()
    {
        return Content("Only for Microsoft employees");
    }
}

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

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

@using System.Security.Claims

@foreach(var claim in User.Claims.ToList())
{
    <p>@claim.Type : @claim.Value</p>
}
<div>Город: @User.FindFirst(x => x.Type == ClaimTypes.Locality).Value</div>
<div>Компания: @User.FindFirst(x => x.Type == "company").Value</div>

Запустим проект и зарегистрируем нового пользователя:

Создание нового пользователя в ASP.NET Core

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

Claims based authorization in ASP.NET Core
Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850