Данное руководство устарело. Актуальное руководство: Руководство по ASP.NET Core 7
Авторизация по ролям позволяет разграничить доступ к ресурсам в зависимости от группы, к которой принадлежит пользователь. ASP.NET Core позволяет легко создать разграничение доступа по ролям.
Для работы с ролями создадим новый проект по типу ASP.NET Core Web App (Model-View-Controller), который назовем RolesApp:
После создания проекта добавим в него в папку Models классы, которые будут представлять пользователя и его роль:
public class User { public int Id { get; set; } public string Email { get; set; } public string Password { get; set; } public int? RoleId { get; set; } public Role Role { get; set; } } public class Role { public int Id { get; set; } public string Name { get; set; } public List<User> Users { get; set; } public Role() { Users = new List<User>(); } }
Данные классы связаны отношением один-ко-многим, то есть один пользователь может иметь только одну роль, а к одной роли могут принадлежать несколько пользователей.
Для взаимодействия с MS SQL Server через Entity Framework добавим в проект через Nuget пакет Microsoft.EntityFrameworkCore.SqlServer. А затем добавим в папку Models новый класс ApplicationContext, который будет представлять контекст данных:
using Microsoft.EntityFrameworkCore; namespace RolesApp.Models { public class ApplicationContext : DbContext { public DbSet<User> Users { get; set; } public DbSet<Role> Roles { get; set; } public ApplicationContext(DbContextOptions<ApplicationContext> options) : base(options) { Database.EnsureCreated(); } protected override void OnModelCreating(ModelBuilder modelBuilder) { string adminRoleName = "admin"; string userRoleName = "user"; string adminEmail = "admin@mail.ru"; string adminPassword = "123456"; // добавляем роли Role adminRole = new Role { Id = 1, Name = adminRoleName }; Role userRole = new Role { Id = 2, Name = userRoleName }; User adminUser = new User { Id = 1, Email = adminEmail, Password = adminPassword, RoleId = adminRole.Id }; modelBuilder.Entity<Role>().HasData(new Role[] { adminRole, userRole }); modelBuilder.Entity<User>().HasData( new User[] { adminUser }); base.OnModelCreating(modelBuilder); } } }
Для инициализации базы данных в методе OnModelCreating()
добавляются в бд две роли и один пользователь - администратора.
Теперь изменим класс Startup для установки и использования контекста данных:
using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.HttpsPolicy; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.EntityFrameworkCore; using Microsoft.AspNetCore.Authentication.Cookies; using RolesApp.Models; namespace RolesApp { public class Startup { public Startup(IConfiguration configuration) { Configuration = configuration; } public IConfiguration Configuration { get; } public void ConfigureServices(IServiceCollection services) { string connection = "Server=(localdb)\\mssqllocaldb;Database=rolesappdb;Trusted_Connection=True;"; services.AddDbContext<ApplicationContext>(options => options.UseSqlServer(connection)); services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme) .AddCookie(options => { options.LoginPath = new Microsoft.AspNetCore.Http.PathString("/Account/Login"); options.AccessDeniedPath = new Microsoft.AspNetCore.Http.PathString("/Account/Login"); }); services.AddControllersWithViews(); } public void Configure(IApplicationBuilder app) { app.UseDeveloperExceptionPage(); app.UseHttpsRedirection(); app.UseStaticFiles(); app.UseRouting(); app.UseAuthentication(); app.UseAuthorization(); app.UseEndpoints(endpoints => { endpoints.MapControllerRoute( name: "default", pattern: "{controller=Home}/{action=Index}/{id?}"); }); } } }
Далее добавим в папку Controllers новый контроллер AccountController, который будет выполнять регистрацию и логин пользователей.
Для регистрации определим в папке Models вспомогательные модели RegisterModel и LoginModel:
using System.ComponentModel.DataAnnotations; namespace RolesApp.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 class LoginModel { [Required(ErrorMessage = "Не указан Email")] public string Email { get; set; } [Required(ErrorMessage = "Не указан пароль")] [DataType(DataType.Password)] public string Password { get; set; } } }
И также для представлений контроллера добавим в папку Views подкаталог Account и поместим в него новое представление Register.cshtml:
@model RolesApp.Models.RegisterModel <h2>Регистрация</h2> <form asp-action="Register" asp-controller="Account" asp-anti-forgery="true"> <div class="validation" asp-validation-summary="ModelOnly"></div> <div> <div> <label asp-for="Email">Введите Email</label><br /> <input type="text" asp-for="Email" /> <span asp-validation-for="Email" /> </div> <div> <label asp-for="Password">Введите пароль</label><br /> <input asp-for="Password" /> <span asp-validation-for="Password" /> </div> <div> <label asp-for="ConfirmPassword">Повторите пароль</label><br /> <input asp-for="ConfirmPassword" /> <span asp-validation-for="ConfirmPassword" /> </div> <div> <input type="submit" value="Регистрация" /> </div> </div> </form>
А также добавим представление Login.cshtml для логина пользователей:
@model RolesApp.Models.LoginModel <h2>Вход на сайт</h2> <a asp-action="Register" asp-controller="Account">Регистрация</a> <form asp-action="Login" asp-controller="Account" asp-anti-forgery="true"> <div class="validation" asp-validation-summary="ModelOnly"></div> <div> <div class="form-group"> <label asp-for="Email">Введите Email</label> <input type="text" asp-for="Email" /> <span asp-validation-for="Email" /> </div> <div class="form-group"> <label asp-for="Password">Введите пароль</label> <input asp-for="Password" /> <span asp-validation-for="Password" /> </div> <div class="form-group"> <input type="submit" value="Войти" class="btn" /> </div> </div> </form>
А в контроллере AccountController определим методы для логина и регистрации:
using System.Collections.Generic; using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; using RolesApp.Models; using Microsoft.EntityFrameworkCore; using System.Security.Claims; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authentication.Cookies; namespace RolesApp.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 }; Role userRole = await _context.Roles.FirstOrDefaultAsync(r => r.Name == "user"); if (userRole != null) user.Role = userRole; _context.Users.Add(user); await _context.SaveChangesAsync(); await Authenticate(user); // аутентификация return RedirectToAction("Index", "Home"); } else ModelState.AddModelError("", "Некорректные логин и(или) пароль"); } return View(model); } [HttpGet] public IActionResult Login() { return View(); } [HttpPost] [ValidateAntiForgeryToken] public async Task<IActionResult> Login(LoginModel model) { if (ModelState.IsValid) { User user = await _context.Users .Include(u => u.Role) .FirstOrDefaultAsync(u => u.Email == model.Email && u.Password == model.Password); if (user != null) { await Authenticate(user); // аутентификация return RedirectToAction("Index", "Home"); } 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(ClaimsIdentity.DefaultRoleClaimType, user.Role?.Name) }; // создаем объект ClaimsIdentity ClaimsIdentity id = new ClaimsIdentity(claims, "ApplicationCookie", ClaimsIdentity.DefaultNameClaimType, ClaimsIdentity.DefaultRoleClaimType); // установка аутентификационных куки await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, new ClaimsPrincipal(id)); } } }
При регистрации пользователю будет присваиваться роль "user", которая, как ожидается, добавляется в базу данных с помощью инициализации в классе Startup.
Ключевым моментом здесь является установка claim в методе Authenticate()
. Кроме claim, который хранит логин пользователя, здесь также устанавливается
и его роль:
var claims = new List<Claim> { new Claim(ClaimsIdentity.DefaultNameClaimType, user.Email), new Claim(ClaimsIdentity.DefaultRoleClaimType, user.Role?.Name) };
Для указания роли здесь применяется тип claim ClaimsIdentity.DefaultRoleClaimType, а в качестве значения для этого типа используется имя роли. По сути больше для установки роли для пользователя ничего не нужно.
В итоге у нас получится следующий проект:
И в конце мы сможем использовать ограничение по ролям. Например:
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Authorization; using System.Security.Claims; namespace RolesApp.Controllers { public class HomeController : Controller { [Authorize(Roles = "admin, user")] public IActionResult Index() { string role = User.FindFirst(x => x.Type == ClaimsIdentity.DefaultRoleClaimType).Value; return Content($"ваша роль: {role}"); } [Authorize(Roles = "admin")] public IActionResult About() { return Content("Вход только для администратора"); } } }
Здесь доступ к методу Index имеют представлители ролей "admin" и "user". А доступ к методу About - только администраторы.
Используя тип claimа ClaimsIdentity.DefaultRoleClaimType
, в методе Index мы можем получить его значение - то есть роль пользователя.