Управление ролями

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

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

Для работы с ролями ASP.NET Core Identity предоставляет класс IdentityRole. В этом классе определяется несколько свойств:

public virtual TKey Id { get; set; }
public virtual string Name { get; set; }
public virtual string NormalizedName { get; set; }
public virtual string ConcurrencyStamp { get; set; }

Основым является свойство Name, которое и хранит название роли.

Возьмем какой-нибудь имеющийся контроллер и применим к нему доступ по ролям. Например:

using Microsoft.AspNetCore.Authorization;

[Authorize(Roles="admin")]
public class HomeController : Controller
{
	// содержимое контроллера
}

В данном случае мы указали, что доступ к контроллеру HomeController будет открыт только для пользователей, которые принадлежат к роли "admin". Но на данный момент у нас нет никакой роли "admin".

Рассмотрим как мы можем управлять ролями и для это продолжим работу с проектом из прошлой темы (либо создадим новый проект с ASP.NET Core Identity). Для администрирования ролями воспользуемся методами классов UserManager и RoleManager.

Вначале добавим в папку ViewModels модель ChangeRoleViewModel:

using Microsoft.AspNetCore.Identity;
using System.Collections.Generic;

public class ChangeRoleViewModel
{
    public string UserId { get; set; }
    public string UserEmail { get; set; }
    public List<IdentityRole> AllRoles { get; set; }
    public IList<string> UserRoles { get; set; }
    public ChangeRoleViewModel()
    {
        AllRoles = new List<IdentityRole>();
        UserRoles = new List<string>();
    }
}

Эта модель позволит управлять всеми ролями для одного пользователя (в ASP.NET Core Identity один пользователь може иметь множество ролей).

Вначале добавим в папку Controllers новый контроллер RolesController:

using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using CustomIdentityApp.Models;
using CustomIdentityApp.ViewModels;

namespace CustomIdentityApp.Controllers
{
    public class RolesController : Controller
    {
        RoleManager<IdentityRole> _roleManager;
        UserManager<User> _userManager;
        public RolesController(RoleManager<IdentityRole> roleManager, UserManager<User> userManager)
        {
            _roleManager = roleManager;
            _userManager = userManager;
        }
        public IActionResult Index() => View(_roleManager.Roles.ToList());

        public IActionResult Create() => View();
        [HttpPost]
        public async Task<IActionResult> Create(string name)
        {
            if (!string.IsNullOrEmpty(name))
            {
                IdentityResult result = await _roleManager.CreateAsync(new IdentityRole(name));
                if (result.Succeeded)
                {
                    return RedirectToAction("Index");
                }
                else
                {
                    foreach (var error in result.Errors)
                    {
                        ModelState.AddModelError(string.Empty, error.Description);
                    }
                }
            }
            return View(name);
        }
		
        [HttpPost]
        public async Task<IActionResult> Delete(string id)
        {
            IdentityRole role = await _roleManager.FindByIdAsync(id);
            if (role != null)
            {
                IdentityResult result = await _roleManager.DeleteAsync(role);
            }
            return RedirectToAction("Index");
        }

        public IActionResult UserList() => View(_userManager.Users.ToList());

        public async Task<IActionResult> Edit(string userId)
        {
            // получаем пользователя
            User user = await _userManager.FindByIdAsync(userId);
            if(user!=null)
            {
                // получем список ролей пользователя
                var userRoles = await _userManager.GetRolesAsync(user);
                var allRoles = _roleManager.Roles.ToList();
                ChangeRoleViewModel model = new ChangeRoleViewModel
                {
                    UserId = user.Id,
                    UserEmail = user.Email,
                    UserRoles = userRoles,
                    AllRoles = allRoles
                };
                return View(model);
            }

            return NotFound();
        }
        [HttpPost]
        public async Task<IActionResult> Edit(string userId, List<string> roles)
        {
            // получаем пользователя
            User user = await _userManager.FindByIdAsync(userId);
            if(user!=null)
            {
                // получем список ролей пользователя
                var userRoles = await _userManager.GetRolesAsync(user);
                // получаем все роли
                var allRoles = _roleManager.Roles.ToList();
                // получаем список ролей, которые были добавлены
                var addedRoles = roles.Except(userRoles);
                // получаем роли, которые были удалены
                var removedRoles = userRoles.Except(roles);

                await _userManager.AddToRolesAsync(user, addedRoles);

                await _userManager.RemoveFromRolesAsync(user, removedRoles);

                return RedirectToAction("UserList");
            }

            return NotFound();
        }
    }
}

В контроллере получаем объекты RoleManager и UserManager, которые передаются через механизм внедрения зависимостей.

Для представлений для данного контроллера сразу определим в папке Views каталог Roles.

Метод Index выводит список ролей. Для их вывода добавим в каталог Views/Roles новое представление Index.cshtml:

@model IEnumerable<Microsoft.AspNetCore.Identity.IdentityRole>

<h2>Список ролей</h2>
<table class="table">
    @foreach (var role in Model)
    {
        <tr>
            <td>@role.Name</td>
            <td>
                <form asp-action="Delete" asp-route-id="@role.Id" method="post">
                    <button type="submit" class="btn btn-sm btn-danger">
                        Удалить
                    </button>
                </form>
            </td>
        </tr>
    }
</table>
<a asp-action="Create">Добавить роль</a>
<a asp-action="UserList">Список пользователей</a>

В методе Create создаем роль и добавляем ее через вызов _roleManager.CreateAsync(). Также добавим для этого метода представление Create.cshtml:

@model string

<div asp-validation-summary="All" class="text-danger"></div>

<form asp-action="Create" method="post">
    <div class="form-group">
        <label for="name">Новая роль</label>
        <input name="name" class="form-control" />
    </div>
    <button type="submit" class="btn btn-primary">Добавить</button>
</form>

В методе Delete по id получаем роль и удаляем ее с помощью вызова метода _roleManager.DeleteAsync().

Отдельно стоит сказать про редактирование. Поскольку класс IdentityRole содержит только два свойства: Id и Name, то особо редактировать здесь нечего. Можно конечно изменять значение свойства Name, но это нежелательно, поскольку в приложении могут использоваться атрибуты Authorize, которые по имени роли устанавливают ограничение доступа. Но, конечно, если бы роль представляла какой-то производный класс от IdentityRole и имела бы какие-то дополнительные свойства, то там было бы больше возможностей по редактированию.

В данном же случае для примера используется редактирование списка ролей, к которым принадлежит определенный пользователь. Для вывода списка пользователей здесь определен метод UserList. Для вывода пользователей добавим следующее представление UserList.cshtml:

@model IEnumerable<CustomIdentityApp.Models.User>

<h2>Список пользователей</h2>
<table class="table">
    @foreach (var user in Model)
    {
        <tr>
            <td>@user.Email</td>
            <td>
                <a class="btn btn-sm btn-primary" asp-action="Edit" asp-route-userid="@user.Id">Права доступа</a>
            </td>
        </tr>
    }
</table>

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

И также добавим для этого метода в папку Views/Roles представление Edit.cshtml:

@using Microsoft.AspNetCore.Identity
@model CustomIdentityApp.ViewModels.ChangeRoleViewModel

<h2>Изменение ролей для пользователя @Model.UserEmail</h2>

<form asp-action="Edit" method="post">
    <input type="hidden" name="userId" value="@Model.UserId" />
    <div class="form-group">
        @foreach (IdentityRole role in Model.AllRoles)
        {
            <input type="checkbox" name="roles" value="@role.Name"
                   @(Model.UserRoles.Contains(role.Name) ? "checked=\"checked\"" : "") />@role.Name <br />
        }
    </div>
    <button type="submit" class="btn btn-primary">Сохранить</button>
</form>

С помощью флажков (<input type="checkbox" >) здесь выводятся все роли. А те роли, для которых добавлен данный пользователь, имеют отмеченные флажки.

В post-версии действия Edit мы ожидаем получить id редактируемого пользователя и список выбранных ролей. С помощью операции разности множеств получаем добавленные и удаленные роли и далее применяем к ним методы _userManager.AddToRolesAsync() и _userManager.RemoveFromRolesAsync().

Основные моменты, добавленные в этой теме в проект:

Ограничение доступа по ролям ASP.NET Core Identity

Добавим несколько ролей и перейдем к методу Index:

Роли в ASP.NET Core Identity

Перейдем к списку пользователей:

Добавление ролей в ASP.NET Core Identity

И изменим для кого-нибудь из них набор ролей:

Изменение ролей в ASP.NET Core Identity
Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850