![Привлекательный login asp. Контроль над кодом аутентификации](https://i0.wp.com/habrastorage.org/storage2/67e/876/397/67e87639765e24cfd8ef5b1cb74242b6.jpg)
Привлекательный login asp. Контроль над кодом аутентификации
Возможно, я ошибаюсь, но после нескольких часов поиска в Интернете и поиска я снова должен попросить прекрасных людей здесь, в StackOverflow. Я пытаюсь добавить контроллер входа на сайт aspx WebForms. Я сделал следующее:
- Добавлены соответствующие значения в WebConfig.
- Настройка мою базу данных с aspnet_regsql (Использование рамки v4.0/Сайт работает под управлением 4.5 (Является ли это проблемой?)
- Добавлен CreateUserWizard и проверяется, если пользователи добавляются в базу данных. (Рабочий)
- Проверено, если идентификатор приложения/Имя такое же, как пользователи получает назначение в базе данных.
Я первый попытался иметь регистрационную форму на моем сайте, используя default.aspx в сочетании с & . Если я поставляю форма с учетными данными, которые соответствуют пользователю, зарегистрированному в Creat eUserWizard, я не получаю ошибки и перенаправляет меня. Но я все еще не вошел в систему. Если я поставлю ложные учетные данные, он дает мне «Неверную ошибку имени пользователя/пароля», поэтому он должен иметь возможность подключаться и проверять учетные данные.
Я немного потерял в этой точке, потому что ошибок не представлено, и я не уверен, как я должен продолжить поиск ошибок. Я также попытался добавить второй сайт под названием «login.aspx» с теми же результатами.
Мой WebConfig
Войти Форма
* *
CreateUserWizard
Что TRIE d после получения несколько советов от вас, ребят:
- Добавлен ярлык, в AnonymouseTemplate , она проходит тест на User.Identity.isAuthenticated , и она возвращает ложь. Даже грубая ошибка не возникает.
- Я установил скрипач на скриншот ниже, он получает файл cookie, когда я отправляю форму для входа.
- Tutorial
Цель урока : Изучить способ авторизации через Cookie, использование стандартных атрибутов доступа к контроллеру и методу контроллера. Использование IPrincipal. Создание собственного модуля (IHttpModule) и собственного фильтра IActionFilter.
Небольшое отступление: На самом деле в asp.net mvc все учебники рекомендуют пользоваться уже придуманной системой авторизации, которая называется AspNetMembershipProvider, она была описана в статье http://habrahabr.ru/post/142711/ (сейчас доступ уже закрыт), но обьяснено это с точки зрения «нажимай и не понимай, что там внутри». При первом знакомстве с asp.net mvc меня это смутило. Далее, в этой статье http://habrahabr.ru/post/143024/ - сказано, что пользоваться этим провайдером – нельзя. И я согласен с этим. Здесь же, мы достаточно глубоко изучаем всякие хитрые asp.net mvc стандартные приемы, так что это один из основных уроков.
Кукисы Кукисы – это часть информации, отсылаемая сервером браузеру, которую браузер возвращает обратно серверу вместе с каждым (почти каждым) запросом.Сервер в заголовок ответа пишет:
Set-Cookie: value[; expires=date][; domain=domain][; path=path][; secure]
Например:
HTTP/1.1 200 OK
Content-type: text/html
Set-Cookie: name=value
Set-Cookie: name2=value2; Expires=Wed, 09-Jun-2021 10:18:14 GMT
Браузер (если не истекло время действия кукиса) при каждом запросе:
GET /spec.html HTTP/1.1
Host: www.example.org
Cookie: name=value; name2=value2
Accept: */*
Устанавливаем cookie (/Areas/Default/Controllers/HomeController.cs):
public ActionResult Index()
{
var cookie = new HttpCookie()
{
Name ="test_cookie",
Value = DateTime.Now.ToString("dd.MM.yyyy"),
Expires = DateTime.Now.AddMinutes(10),
};
Response.SetCookie(cookie);
return View();
}
В Chrome проверяем установку:
Для получения кукисов:
var cookie = Request.Cookies["test_cookie"];
Делаем точку остановки и проверяем:
Примечание: подробнее можно изучить кукисы по следующей ссылке:
http://www.nczonline.net/blog/2009/05/05/http-cookies-explained/
- FormsAuthenticationTicket – мы воспользуемся этим классом, чтобы хранить данные авторизации в зашифрованном виде
- Нужно реализовать интерфейс IPrincipal и установить в HttpContext.User для проверки ролей и IIdentity интерфейса.
- Для интерфейса IIdentity сделать реализацию
- Вывести в BaseController в свойство CurrentUser значение пользователя, который сейчас залогинен.
Приступим.
Создадим интерфейс IAuthentication и его реализацию CustomAuthentication (/Global/Auth/IAuthentication.cs):
Public interface IAuthentication { /// /// Конекст (тут мы получаем доступ к запросу и кукисам) /// HttpContext HttpContext { get; set; } User Login(string login, string password, bool isPersistent); User Login(string login); void LogOut(); IPrincipal CurrentUser { get; } }
Реализация (/Global/Auth/CustomAuthentication.cs):
public class CustomAuthentication: IAuthentication
{
private static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
private const string cookieName = "__AUTH_COOKIE";
public HttpContext HttpContext { get; set; }
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;
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 (/Global/Auth/UserProvider.cs):
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 (/Global/Auth/UserIdentity.cs):
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() (LessonProject.Model:/SqlRepository/User.cs):
Public User GetUser(string email) { return Db.Users.FirstOrDefault(p => string.Compare(p.Email, email, true) == 0); }
Почти все готово. Выведем CurrentUser в BaseController:
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
{
///
/// Текщий пользователь
///
public User User { get; set; }
…
public IAuthentication Auth { get; set; }
public User CurrentUser
{
get
{
return ((IUserProvider)Auth.CurrentUser.Identity).User;
}
}
А теперь попробуем инициализировать это всё.
Вначале добавим наш IAuthentication + CustomAuthentication в регистрацию к Ninject (/App_Start/NinjectWebCommon.cs):
Kernel.Bind().To().InRequestScope();
Потом создадим модуль, который будет на событие AuthenticateRequest совершать действие авторизации:
public class AuthHttpModule: IHttpModule
{
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();
auth.HttpContext = context;
context.User = auth.CurrentUser;
}
public void Dispose()
{
}
}
Вся соль в строках: auth.HttpContext = context и context.User = auth.CurrentUser . Как только наш модуль авторизации узнает о контексте и содержащихся в нем кукисах, ту же моментально получает доступ к имени, по нему он в репозитории получает данныепользователя и возвращает в BaseController. Но не сразу всё, а по требованию.
Подключаем модуль в Web.config:
…
План таков:
- Наверху показываем, авторизован пользователь или нет. Если авторизован, то его email и ссылка на выход, если нет, то ссылки на вход и регистрацию
- Создаем форму для входа
- Если пользователь правильно ввел данные – то авторизуем его и отправляем на главную страницу
- Если пользователь выходит – то убиваем его авторизацию
Поехали. Добавляем Html.Action(“UserLogin”, “Home”) – это partial view (т.е. кусок кода, который не имеет Layout) – т.е. выводится где прописан, а не в RenderBody().
_Layout.cshtml (/Areas/Default/Views/Shared/_Layout.cshtml):
@RenderBody() HomeController.cs: public ActionResult UserLogin() { return View(CurrentUser); }
UserLogin.cshtml (/Areas/Default/Views/Home/UserLogin.cshtml):
@model LessonProject.Model.User @if (Model != null) {
Контроллер входа выхода LoginController (/Areas/Default/Controllers/LoginController.cs):
Public class LoginController: DefaultController { public ActionResult Index() { return View(new LoginView()); } 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 (/Models/ViewModels/LoginView.cs):
public class LoginView
{
public string Email { get; set; }
public string Password { get; set; }
public bool IsPersistent { get; set; }
}
Страница для входа Index.cshtml (/Areas/Default/Views/Index.cshtml):
@model LessonProject.Models.ViewModels.LoginView @{ ViewBag.Title = "Вход"; Layout = "~/Areas/Default/Views/Shared/_Layout.cshtml"; } Вход @using (Html.BeginForm("Index", "Login", FormMethod.Post, new { @class = "form-horizontal" })) { Вход Email @Html.TextBox("Email", Model.Email, new { @class = "input-xlarge" })
Введите Email
@Html.ValidationMessage("Email") Пароль @Html.Password("Password", Model.Password, new { @class = "input-xlarge" }) @Html.ValidationMessage("Password") Войти }Запускаем и проверяем:
Все исходники находятся по адресу
В составе технологии ASP.Net Web Forms имеется набор ЭУ для создания и использования учетных записей пользователей:
- Login – ЭУ для подключения пользователя (ввод имени и пароля) и проверки его соответствия данным, которые содержатся в БД. Если данные совпадают (т. е. пользователь прошел аутентификацию, то выполняется переход к запрашиваемой странице).
- LoginView – позволяет показывать разную информацию для подключенных пользователей. Например, можно использовать эту страницу для отображения информации, которая доступна только аутентифицированным пользователям.
- LoginStatus – ЭУ, показывающий ссылку на страницу подключения для пользователей, которые не были аутентифицированы (Logout), и ссылку на страницу отключения для подключенных пользователей (Login).
- LoginName – ЭУ, показывающий текущее имя пользователя, если он подключен к системе.
- Password Recovery – ЭУ для выполнения восстановления пароля пользователей путем отправки e-mail-сообщсния или при ответе пользователя на секрет ный вопрос.
- CreateUserWizard – ЭУ, который собирает информацию о новом пользователе и создает в БД новую учетную запись.
- ChangePassword – ЭУ, позволяющий подключенному пользователю сменить пароль.
Элемент управления Login предоставляет готовый к использованию интерфейс, который запрашивает имя и пароль пользователя. Он включает кнопку с атрибутом CommandName-"Login” для подключения пользователя. При нажатии пользователем на данную кнопку ЭУ автоматически выполняет проверку соответствия введенного имени и пароля пользователя с данными, содержащимися в БД, а затем вызывает переход к запрашиваемой web-форме приложения. Данный ЭУ является полностью расширяемым и позволяет переопределить его разметку, стиль и свойства, а также самому обрабатывать события, чтобы изменить стандартное поведение. Пример настройки описания ЭУ Login показан ниже:
Подключение к системе
user.aspx
user
![]() чтобы вы всегда были в курсе самого интересного. |