Данное руководство устарело. Актуальное руководство: Руководство по ASP.NET Core
Универсальные провайдеры предоставляют уже готовый функционал авторизации. Но в то же время эти провайдеры обладают достаточной гибкостью - в частности мы можем их переопределить по своему усмотрению. При этом нам необязательно переопределять и использовать все четыре провайдера. Что довольно удобно, особенно в ситуации, когда нам не нужны все навороты ASP.NET Identity, а требуется построить очень простенькую систему авторизации.
Итак, создадим свой провайдер ролей. Для этого вначале создадим новый проект и добавим в него через NuGet Entity Framework.
Первым делом определим модели, которые будут описывать учетные записи. Это у нас будут модели пользователей и ролей, а также контекст данных для доступа к ним. Поэтому добавим в папку Models следующие классы:
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.ComponentModel.DataAnnotations; using System.Data.Entity; namespace CustomRoleProviderApp.Models { public class UserContext : DbContext { public UserContext() : base("DefaultConnection") { } public DbSet<User> Users { get; set; } public DbSet<Role> Roles { get; set; } } 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 class UserDbInitializer : DropCreateDatabaseAlways<UserContext> { protected override void Seed(UserContext db) { Role admin = new Role { Name = "admin" }; Role user = new Role { Name = "user" }; db.Roles.Add(admin); db.Roles.Add(user); db.Users.Add(new User { Email="somemail@gmail.com", Password="123456", Role = admin }); base.Seed(db); } } }
Здесь определен класс пользователя и класс ролей. Класс пользователя содержит ссылку на выполняемую роль в системе. И также определен контекст UserContext, через который мы будем получать данные из БД. Контекст данных связывается с подключение к бд, которое определено в файле web.config. При желании можно определить любое другое подключение.
Кроме того, чтобы инициализировать базу данных начальными данными, через класс инициализатора UserDbInitializer мы добавляем пару ролей и одного пользователя в базу данных. И чтобы инициализатор сработал, добавим его вызов в файл global.asax:
public class MvcApplication : System.Web.HttpApplication { protected void Application_Start() { Database.SetInitializer(new UserDbInitializer()); //............................................ } }
Теперь определим сам провайдер ролей. Для этого сначала добавим в проект папку Providers и затем в него добавим новый класс CustomRoleProvider:
using System; using System.Linq; using System.Web.Security; using CustomRoleProviderApp.Models; using System.Data.Entity; namespace CustomRoleProviderApp.Providers { public class CustomRoleProvider : RoleProvider { public override string[] GetRolesForUser(string username) { string[] roles = new string[] { }; using (UserContext db = new UserContext()) { // Получаем пользователя User user = db.Users.Include(u=>u.Role).FirstOrDefault(u => u.Email == username); if (user != null && user.Role!=null) { // получаем роль roles = new string[] { user.Role.Name }; } return roles; } } public override void CreateRole(string roleName) { throw new NotImplementedException(); } public override bool IsUserInRole(string username, string roleName) { using (UserContext db = new UserContext()) { // Получаем пользователя User user = db.Users.Include(u=>u.Role).FirstOrDefault(u => u.Email == username); if (user!=null && user.Role!=null && user.Role.Name == roleName) return true; else return false; } } public override void AddUsersToRoles(string[] usernames, string[] roleNames) { throw new NotImplementedException(); } public override string ApplicationName { get { throw new NotImplementedException(); } set { throw new NotImplementedException(); } } public override bool DeleteRole(string roleName, bool throwOnPopulatedRole) { throw new NotImplementedException(); } public override string[] FindUsersInRole(string roleName, string usernameToMatch) { throw new NotImplementedException(); } public override string[] GetAllRoles() { throw new NotImplementedException(); } public override string[] GetUsersInRole(string roleName) { throw new NotImplementedException(); } public override void RemoveUsersFromRoles(string[] usernames, string[] roleNames) { throw new NotImplementedException(); } public override bool RoleExists(string roleName) { throw new NotImplementedException(); } } }
В целях демонстрации переопределено три метода. Первый из них - GetRolesForUser позволяет получать набор ролей для определенного пользователя. Второй метод - - IsUserInRole - определяет, выполняет ли пользователь определенную роль в системе.
Чтобы использовать провайдер ролей в приложении, надо добавить его определение в файл конфигурации. Откроем файл web.config
и в пределах узла system.web
добавим наш провайдер:
<authentication mode="Forms"> <forms name="cookies" loginUrl="~/Account/Login" /> </authentication> <roleManager enabled="true" defaultProvider="MyRoleProvider"> <providers> <add name="MyRoleProvider" type="CustomRoleProviderApp.Providers.CustomRoleProvider" /> </providers> </roleManager>
И в конце добавим контроллер AccountController для логина и регистрации:
using System.Linq; using System.Web.Mvc; using System.Web.Security; using CustomRoleProviderApp.Models; namespace CustomRoleProviderApp.Controllers { public class AccountController : Controller { UserContext db = new UserContext(); public ActionResult Login() { return View(); } [HttpPost] [ValidateAntiForgeryToken] public ActionResult Login(LoginModel model) { if (ModelState.IsValid) { // поиск пользователя в бд User user = db.Users.FirstOrDefault(u => u.Email == model.Name && u.Password == model.Password); if (user != null) { FormsAuthentication.SetAuthCookie(model.Name, true); return RedirectToAction("Index", "Home"); } else { ModelState.AddModelError("", "Пользователя с таким логином и паролем нет"); } } return View(model); } public ActionResult Register() { return View(); } [HttpPost] [ValidateAntiForgeryToken] public ActionResult Register(RegisterModel model) { if (ModelState.IsValid) { User user = db.Users.FirstOrDefault(u => u.Email == model.Name && u.Password == model.Password); if (user == null) { db.Users.Add(new User { Email = model.Name, Password = model.Password, RoleId = 2 }); db.SaveChanges(); user = db.Users.Where(u => u.Email == model.Name && u.Password == model.Password).FirstOrDefault(); if (user != null) { FormsAuthentication.SetAuthCookie(model.Name, true); return RedirectToAction("Index", "Home"); } } else ModelState.AddModelError("", "Пользователь с таким логином уже существует"); } return View(model); } public ActionResult Logoff() { FormsAuthentication.SignOut(); return RedirectToAction("Index", "Home"); } } }
В методе Register при добавлении нового пользователя мы ему будем присваивать в роль user. Модели, используемые методами Login и Register, выглядят так:
public class RegisterModel { [Required] public string Name { get; set; } [Required] [DataType(DataType.Password)] public string Password { get; set; } [Required] [DataType(DataType.Password)] [Compare("Password", ErrorMessage = "Пароли не совпадают")] public string ConfirmPassword { get; set; } } public class LoginModel { [Required] public string Name { get; set; } [Required] [DataType(DataType.Password)] public string Password { get; set; } }
И теперь мы можем разграничить доступ к методам различных контроллеров с помощью атрибута Authorize:
public class HomeController : Controller { [Authorize(Roles = "admin")] public ActionResult Index() { return View(); } }