Главная страница Случайная страница КАТЕГОРИИ: АвтомобилиАстрономияБиологияГеографияДом и садДругие языкиДругоеИнформатикаИсторияКультураЛитератураЛогикаМатематикаМедицинаМеталлургияМеханикаОбразованиеОхрана трудаПедагогикаПолитикаПравоПсихологияРелигияРиторикаСоциологияСпортСтроительствоТехнологияТуризмФизикаФилософияФинансыХимияЧерчениеЭкологияЭкономикаЭлектроника |
Авторизация
В нашем случае авторизация будет основана на использовании кукисов. Для этого изучим следующие положения: · FormsAuthenticationTicket – мы воспользуемся этим классом, чтобы хранить данные авторизации в зашифрованном виде · Нужно реализовать интерфейс IPrincipal и установить в HttpContext.User для проверки ролей и IIdentity интерфейса. · Для интерфейса IIdentity сделать реализацию · Вывести в BaseController в свойство CurrentUser значение пользователя, который сейчас залогинен. Приступим. Создадим интерфейс IAuthentication и его реализацию CustomAuthentication public interface IAuthentication { /// < summary> /// Конекст (тут мы получаем доступ к запросу и кукисам) /// < /summary> HttpContext HttpContext { get; set; }
User Login(string login, string password, bool isPersistent);
User Login(string login);
void LogOut();
IPrincipal CurrentUser { get; } } Реализация: public class CustomAuthentication: IAuthentication { private static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
private const string cookieName = " __AUTH_COOKIE";
public HttpContext HttpContext { get; set; }
[Inject] public IRepository Repository { get; set; }
#region IAuthentication Members
public User Login(string userName, string Password, bool isPersistent) { User retUser = Repository.Login(userName, Password); if (retUser! = null) { CreateCookie(userName, isPersistent); } return retUser; }
public User Login(string userName) { User retUser = Repository.Users.FirstOrDefault(p => string.Compare(p.Email, userName, true) == 0); if (retUser! = null) { CreateCookie(userName); } return retUser; }
private void CreateCookie(string userName, bool isPersistent = false) { var ticket = new FormsAuthenticationTicket( 1, userName, DateTime.Now, DateTime.Now.Add(FormsAuthentication.Timeout), isPersistent, string.Empty, FormsAuthentication.FormsCookiePath);
// Encrypt the ticket. var encTicket = FormsAuthentication.Encrypt(ticket);
// Create the cookie. var AuthCookie = new HttpCookie(cookieName) { Value = encTicket, Expires = DateTime.Now.Add(FormsAuthentication.Timeout) }; HttpContext.Response.Cookies.Set(AuthCookie); }
public void LogOut() { var httpCookie = HttpContext.Response.Cookies[cookieName]; if (httpCookie! = null) { httpCookie.Value = string.Empty; } }
private IPrincipal _currentUser;
public IPrincipal CurrentUser { get { if (_currentUser == null) { try { HttpCookie authCookie = HttpContext.Request.Cookies.Get(cookieName); if (authCookie! = null & &! string.IsNullOrEmpty(authCookie.Value)) { var ticket = FormsAuthentication.Decrypt(authCookie.Value); _currentUser = new UserProvider(ticket.Name, Repository); } else { _currentUser = new UserProvider(null, null); } } catch (Exception ex) { logger.Error(" Failed authentication: " + ex.Message); _currentUser = new UserProvider(null, null); } } return _currentUser; } } #endregion } Суть сводится к следующему, мы, при инициализации запроса, получаем доступ к HttpContext.Request.Cookies и инициализируем UserProvider: var ticket = FormsAuthentication.Decrypt(authCookie.Value); _currentUser = new UserProvider(ticket.Name, Repository);
Для авторизации в IRepository добавлен новый метод IRepository.Login. Реализация в SqlRepository: public User Login(string email, string password) { return Db.Users.FirstOrDefault(p => string.Compare(p.Email, email, true) == 0 & & p.Password == password); } UserProvider, собственно, реализует интерфейс IPrincipal (в котором есть проверка ролей и доступ к IIdentity). Рассмотрим класс UserProvider: public class UserProvider: IPrincipal { private UserIndentity userIdentity { get; set; }
#region IPrincipal Members
public IIdentity Identity { get { return userIdentity; } }
public bool IsInRole(string role) { if (userIdentity.User == null) { return false; } return userIdentity.User.InRoles(role); }
#endregion
public UserProvider(string name, IRepository repository) { userIdentity = new UserIndentity(); userIdentity.Init(name, repository); }
public override string ToString() { return userIdentity.Name; } Наш UserProvider знает про то, что его IIdentity классом есть UserIdentity, а поэтому знает про класс User, внутри которого мы реализуем метод InRoles(role): public bool InRoles(string roles) { if (string.IsNullOrWhiteSpace(roles)) { return false; }
var rolesArray = roles.Split(new[] { ", " }, StringSplitOptions.RemoveEmptyEntries); foreach (var role in rolesArray) { var hasRole = UserRoles.Any(p => string.Compare(p.Role.Code, role, true) == 0); if (hasRole) { return true; } } return false; } В метод InRoles мы ожидаем, что придет запрос о ролях, которые допущены к ресурсу, разделенные запятой. Т.е., например, “admin, moderator, editor”, если хотя бы одна из ролей есть у нашего User – то возвращаем зачение «истина» (доступ есть). Сравниваем по полю Role.Code, а не Role.Name. Рассмотрим класс UserIdentity: public class UserIndentity: IIdentity { public User User { get; set; }
public string AuthenticationType { get { return typeof(User).ToString(); } }
public bool IsAuthenticated { get { return User! = null; } }
public string Name { get { if (User! = null) { return User.Email; } //иначе аноним return " anonym"; } }
public void Init(string email, IRepository repository) { if (! string.IsNullOrEmpty(email)) { User = repository.GetUser(email); } } } В IRepository добавляем новый метод GetUser(email). Реализация для SqlRepository.GetUser(): public User GetUser(string email) { return Db.Users.FirstOrDefault(p => string.Compare(p.Email, email, true) == 0); }
Почти все готово. Выведем CurrentUser в BaseController: [Inject] public IAuthentication Auth { get; set; } public User CurrentUser { get { return ((UserIndentity)Auth.CurrentUser.Identity).User; } } Да, это не очень правильно, так как здесь присутствует сильное связывание. Поэтому сделаем так, введем еще один интерфейс IUserProvider, из которого мы будем требовать вернуть нам авторизованного User: public interface IUserProvider { User User { get; set; } } … public class UserIndentity: IIdentity, IUserProvider { /// < summary> /// Текщий пользователь /// < /summary> public User User { get; set; } … [Inject] public IAuthentication Auth { get; set; }
public User CurrentUser { get { return ((IUserProvider)Auth.CurrentUser.Identity).User; } } А теперь попробуем инициализировать это всё. Вначале добавим наш IAuthentication + CustomAuthentication в регистрацию к Ninject: kernel.Bind< IAuthentication> ().To< CustomAuthentication> ().InRequestScope(); Потом создадим модуль, который будет на событие AuthenticateRequest совершать действие авторизации: public class AuthHttpModule: IHttpModule[c2] { public void Init(HttpApplication context) { context.AuthenticateRequest += new EventHandler(this.Authenticate); }
private void Authenticate(Object source, EventArgs e) { HttpApplication app = (HttpApplication)source; HttpContext context = app.Context;
var auth = DependencyResolver.Current.GetService< IAuthentication> (); auth.HttpContext = context; context.User = auth.CurrentUser; }
public void Dispose() { } } Вся соль в выделенных строках: auth.HttpContext = context и context.User = auth.CurrentUser. Как только наш модуль авторизации узнает о контексте и содержащихся в нем кукисах, ту же моментально получает доступ к имени, по нему он в репозитории получает данныепользователя и возвращает в BaseController. Но не сразу всё, а по требованию. Подключаем модуль в Web.config: < system.web> … < httpModules> < add name=" AuthHttpModule" type=" LessonProject.Global.Auth.AuthHttpModule" /> < /httpModules> < /system.web> План таков: · Наверху показываем, авторизован пользователь или нет. Если авторизован, то его email и ссылка на выход, если нет, то ссылки на вход и регистрацию · Создаем форму для входа · Если пользователь правильно ввел данные – то авторизуем его и отправляем на главную страницу · Если пользователь выходит – то убиваем его авторизацию Поехали. Добавляем Html.Action(“UserLogin”, “Home”) – это partial view (т.е. кусок кода, который не имеет Layout) – т.е. выводится где прописан, а не в RenderBody(). _Layout.cshtml: < body>
< div class=" navbar navbar-fixed-top" > < div class=" navbar-inner" > < div class=" container" > < ul class=" nav nav-pills pull-right" > @Html.Action(" UserLogin", " Home") < /ul> < /div> < /div> < /div>
@RenderBody()
HomeController.cs: public ActionResult UserLogin() { return View(CurrentUser); } UserLogin.cshtml: @model LessonProject.Model.User
@if (Model! = null) { < li> @Model.Email< /li> < li> @Html.ActionLink(" Выход", " Logout", " Login")< /li> } else { < li> @Html.ActionLink(" Вход", " Index", " Login")< /li> < li> @Html.ActionLink(" Регистрация", " Register", " User")< /li> }
Контроллер входа выхода LoginController: public class LoginController: DefaultController { [HttpGet] public ActionResult Index() { return View(new LoginView()); }
[HttpPost] public ActionResult Index(LoginView loginView) { if (ModelState.IsValid) { var user = Auth.Login(loginView.Email, loginView.Password, loginView.IsPersistent); if (user! = null) { return RedirectToAction(" Index", " Home"); } ModelState[" Password" ].Errors.Add(" Пароли не совпадают"); } return View(loginView); }
public ActionResult Logout() { Auth.LogOut(); return RedirectToAction(" Index", " Home"); } } LoginView.cs: public class LoginView { [Required(ErrorMessage = " Введите email")] public string Email { get; set; }
[Required(ErrorMessage = " Введите пароль")] public string Password { get; set; }
public bool IsPersistent { get; set; } }
Страница для входа Index.cshtml: @model LessonProject.Models.ViewModels.LoginView @{ ViewBag.Title = " Вход"; Layout = " ~/Areas/Default/Views/Shared/_Layout.cshtml"; }
< h2> Вход< /h2>
@using (Html.BeginForm(" Index", " Login", FormMethod.Post, new { @class = " form-horizontal" })) { < fieldset> < legend> Вход< /legend> < div class=" control-group" > < label class=" control-label" for=" Email" > Email< /label> < div class=" controls" > @Html.TextBox(" Email", Model.Email, new { @class = " input-xlarge" }) < p class=" help-block" > Введите Email< /p> @Html.ValidationMessage(" Email") < /div>
< /div> < div class=" control-group" > < label class=" control-label" for=" Password" > Пароль< /label> < div class=" controls" > @Html.Password(" Password", Model.Password, new { @class = " input-xlarge" }) @Html.ValidationMessage(" Password") < /div> < /div> < div class=" form-actions" > < button type=" submit" class=" btn btn-primary" > Войти< /button> < /div> < /fieldset> } Запускаем и проверяем: После авторизации:
|