Содержание
С темой утверждений (Claims) в ASP.NET Core мы уже сталкивались, когда изучали «чистый» ASP.NET Core, правда эта тема прошла немного вскользь. Здесь мы попытаемся более детально разобраться с тем, что из себя представляют утверждения в ASP.NET Core Identity и как они используются в системе аутентификации и авторизации пользователей ASP.NET Core MVC.
Что такое «утверждение» (claim) в ASP.NET Core Identity
В ASP.NET Core Identity утверждения (claims) — это пары «ключ-значение» на основании которых пользователю даётся разрешение на выполнение каких-либо действий. К примеру, вы поступили в ВУЗ и вам выдан студенческий билет 1 сентября 2023 года. В этом случае, в терминах ASP.NET Core Identity мы могли бы определить следующее утверждение:
- Имя = студенческий билет
- Значение = 1 сентября 2023 года
- Кто выдал (эмитент) = название вашего ВУЗа
Это значит, что Вы вправе (получаете разрешение) заходить в корпуса конкретного учебного заведения (выполнять действие), предъявляя студенческий билет (утверждение), до тех пор, пока вы его не сдадите в отдел кадров (пока разрешение не отозвано эмитентом).
Вообще, даже роли пользователей, с которыми мы разбирались в предыдущей части, строго говоря, тоже представляют собой утверждения (claim), хоть и выделяются отдельно. Но здесь стоит отметить, что хоть каждая роль — это утверждение, однако, не каждое утверждение — это роль. Утверждение (claim) — более широкое понятие, используемое для аутентификации и авторизации пользователей.
Просмотр утверждений пользователя
Начнем с простого — просмотр уже имеющихся утверждений для пользователя. Для этого создадим в папке Areas/Account/Controllers новый контроллер с именем ClaimsController
и переопределим его действие Index
следующим образом:
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; namespace AspMvcAuth.Areas.Account.Controllers { [Authorize] public class ClaimsController : Controller { [Route("{area}/{controller}")] public IActionResult Index() { return View(User?.Claims); } } }
Мы получаем утверждения пользователя через свойство User объекта HttpContext, который доступен в контроллере. Это свойство возвращает объект ClaimsPrincipal для текущего пользователя, который содержит все утверждения. Здесь стоит отметить, что доступ к действиям контроллера будут иметь только аутентифицированные пользователи, о чем говорит атрибут [Authorize]
контроллера. При вызове действия Index()
мы будем возвращать в одноименное представление список утверждений для пользователя.
Теперь создадим необходимо представление. Для этого создадим новую папку Areas/Account/Views/Claims:
И разместим в этой папке новое представление Index.cshtml со следующим содержимым:
@model IEnumerable<System.Security.Claims.Claim> @{ ViewData["Title"] = "Утверждения"; } <h2 class="bg-primary m-1 p-1 text-white">Утверждения</h2> <table class="table table-sm table-bordered"> <tr> <th>Кому принадлежит</th> <th>Эмитент</th> <th>Тип</th> <th>Значение</th> </tr> @foreach (var claim in Model.OrderBy(x => x.Type)) { <tr> <td>@claim.Subject.Name</td> <td>@claim.Issuer</td> <td>@claim.Type</td> <td>@claim.Value</td> </tr> } </table>
Это представление, следуя правилам маршрутизации, будет доступно по пути account/claims, добавим эту ссылку в шаблон _Layout.cshtml
<li class="nav-item"> <a class="nav-link text-dark" asp-area="Account" asp-controller="Claims" asp-action="Index">Утверждения</a> </li>
Запустим приложение, авторизуемся и посмотрим на результат:
Как было сказано выше, роли также относятся к утверждениям пользователя, о чем свидетельствует и запись в таблице. Теперь мы можем создавать и удалять различные утверждения пользователя. Для этого, создадим ещё одно представление и назовем его Create.cshtml:
В этом представлении будет содержаться форма для создания нового утверждения для пользователя:
@{ ViewData["Title"] = "Новое утверждение"; } <h1 class="bg-info text-white">Новое утверждение</h1> <a asp-area="Account" asp-controller="Claims" asp-action="Index" class="btn btn-secondary">Вернуться</a> <div asp-validation-summary="All" class="text-danger"></div> <form method="post"> <div class="form-group"> <label for="ClaimType">Тип:</label> <input name="ClaimType" class="form-control" /> </div> <div class="form-group"> <label for="ClaimValue">Значение:</label> <input name="ClaimValue" class="form-control" /> </div> <button type="submit" class="btn btn-primary">Создать</button> </form>
Теперь внесем небольшое изменение в предыдущее представление Index.cshtml добавив необходимые элементы управления утверждениями:
@model IEnumerable<System.Security.Claims.Claim> @{ ViewData["Title"] = "Утверждения"; } <h2 class="bg-primary m-1 p-1 text-white">Утверждения</h2> <a asp-area="Account" asp-controller="Claims" asp-action="Create" class="btn btn-secondary">Добавить утверждение</a> <table class="table table-sm table-bordered"> <tr> <th>Кому принадлежит</th> <th>Эмитент</th> <th>Тип</th> <th>Значение</th> <th>Удалить</th> </tr> @foreach (var claim in Model.OrderBy(x => x.Type)) { <tr> <td>@claim.Subject.Name</td> <td>@claim.Issuer</td> <td>@claim.Type</td> <td>@claim.Value</td> <td> <form asp-action="Delete" method="post"> <input type="hidden" name="claimValues" value="@claim.Type;@claim.Value;@claim.Issuer" /> <button type="submit" class="btn btn-sm btn-danger"> Delete </button> </form> </td> </tr> } </table>
Здесь мы добавили ссылку»Добавить утверждение»:
<a asp-action="Create" class="btn btn-secondary">Добавить утверждение</a>
а также форму удаления утверждения
<form asp-action="Delete" method="post"> <input type="hidden" name="claimValues" value="@claim.Type;@claim.Value;@claim.Issuer" /> <button type="submit" class="btn btn-sm btn-danger"> Delete </button> </form>
При этом, обратите внимание та скрытый элемент input — в нем мы будем передавать тип, значение и эмитента утверждения для того, чтобы можно было произвести его удаление из хранилища. Теперь наше представление Index.cshtml будет выглядеть следующим образом:
Создадим в контроллере необходимые действия. Действие для загрузки представления Create.cshtml
[Route("{area}/{controller}/{action}")] [HttpGet] public IActionResult Create() { return View(); }
Действие для создания нового утверждения:
[Route("{area}/{controller}/{action}")] [HttpPost] public async Task<IActionResult> Create([FromServices]UserManager<ApplicationUser> users, string claimType, string claimValue) { var user = await users.GetUserAsync(HttpContext.User); Claim claim = new(claimType, claimValue, ClaimValueTypes.String); IdentityResult result = await users.AddClaimAsync(user, claim); if (result.Succeeded) return LocalRedirect("/account/claims"); else return BadRequest(result); }
здесь стоит обратить внимание на то, что сервис менеджера пользователей запрашивается непосредственно в методе контроллера. Вы можете создать конструктор контроллера, через который и запрашивать необходимые зависимости. В этом действии мы ищем пользователя в хранилище, создаем новый объект утверждения и добавляем это утверждение к пользователю, используя метод менеджера:
IdentityResult result = await users.AddClaimAsync(user, claim);
Для удаления утверждения используется следующее действие:
[HttpPost] [Route("{area}/{controller}/{action}")] public async Task<IActionResult> Delete([FromServices] UserManager<ApplicationUser> users, string claimValues) { var user = await users.GetUserAsync(HttpContext.User); string[] claimValuesArray = claimValues.Split(";"); string claimType = claimValuesArray[0]; string claimValue = claimValuesArray[1]; string claimIssuer = claimValuesArray[2]; Claim claim = User.Claims.Where(x => x.Type == claimType && x.Value == claimValue && x.Issuer == claimIssuer).FirstOrDefault(); IdentityResult result = await users.RemoveClaimAsync(user, claim); if (result.Succeeded) return LocalRedirect("/account/claims"); else return BadRequest(result); }
Здесь мы по полученным сведениям об утверждении, ищем его в списке утверждений пользователя, используя LINQ:
Claim claim = User.Claims.Where(x => x.Type == claimType && x.Value == claimValue && x.Issuer == claimIssuer).FirstOrDefault();
и пытаемся его удалить, используя метод менеджера пользователей:
IdentityResult result = await users.RemoveClaimAsync(user, claim);
Теперь можно протестировать работу приложения с утверждениями пользователей.
Проверка работы с утверждениями пользователей
Добавим новое утверждение для пользователя:
Новое утверждение появится в базе данных в таблице AspNetUserClaims:
Для того, чтобы увидеть это утверждение в списке, необходимо выйти из приложения и снова аутентифицироваться. Также, при удалении утверждения мы должны перезайти в систему.
Итого
В этой части мы научились работать с утверждениями пользователя — считывать список утверждений, создавать и удалять их. Как было сказано ранее, утверждения используются для авторизации пользователей в ASP.NET Core Identity, однако, чтобы использовать утверждения для этих целей мы также должны создать свои политики авторизации. О том, как работать с политиками авторизации мы узнаем в следующей части
Скачать код проекта из Github