Содержание
С темой утверждений (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







