Commit 9a828b02 authored by SergeevaAA's avatar SergeevaAA

Реализована регистрация и аутентификация на основе куки

Добавлена страница AccessDenied.
parent fa450e9c
namespace FileDesk.Domain.Models
{
using FileDesk.Domain.Enums;
using System;
using System.ComponentModel.DataAnnotations;
using FileDesk.Domain.Enums;
/// <summary>
/// Файл
/// </summary>
......
namespace FileDesk.Domain.ViewModels
{
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
/// <summary>
/// Вью-модель для аутентификации пользователя
/// </summary>
public class LoginViewModel
{
/// <summary>
/// Логин
/// </summary>
[DisplayName("Логин")]
[Required(ErrorMessage = "Не указан логин")]
public string Login { get; set; }
/// <summary>
/// Пароль
/// </summary>
[DisplayName("Пароль")]
[Required(ErrorMessage = "Не указан пароль")]
[DataType(DataType.Password)]
public string Password { get; set; }
}
}
\ No newline at end of file
namespace FileDesk.Domain.ViewModels
{
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
/// <summary>
/// Вью-модель для регистрации пользователя
/// </summary>
public class RegisterViewModel
{
/// <summary>
/// Логин
/// </summary>
[DisplayName("Логин")]
[Required(ErrorMessage = "Не указан логин")]
public string Login { get; set; }
/// <summary>
/// Пароль
/// </summary>
[DisplayName("Пароль")]
[Required(ErrorMessage = "Не указан пароль")]
[DataType(DataType.Password)]
public string Password { get; set; }
/// <summary>
/// Подтверждение пароля
/// </summary>
[DisplayName("Подтверждение пароля")]
[DataType(DataType.Password)]
[Compare("Password", ErrorMessage = "Пароль и его подтверждение не совпадают")]
public string ConfirmPassword { get; set; }
}
}
\ No newline at end of file
......@@ -4,4 +4,8 @@
<TargetFramework>netcoreapp3.1</TargetFramework>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\FileDesk.DataAccess\FileDesk.DataAccess.csproj" />
</ItemGroup>
</Project>
namespace FileDesk.Services
{
using System.Security.Claims;
using FileDesk.Domain.ViewModels;
/// <summary>
/// Интерфейс сервиса для работы с пользователями
/// </summary>
public interface IUserService
{
/// <summary>
/// Проверка существования пользователя
/// </summary>
/// <param name="login">Логин</param>
/// <returns>Существует ли пользователь</returns>
bool IsExist(string login);
/// <summary>
/// Проверка корректности ввода логина и пароля
/// </summary>
/// <param name="login">Логин</param>
/// <param name="password">Пароль</param>
/// <returns>Существует ли в БД комбинация логина и пароля</returns>
bool IsCorrect(string login, string password);
/// <summary>
/// Регистрация пользователя
/// </summary>
/// <param name="registerViewModel">Вью-модель для регистрации пользователя</param>
void Register(RegisterViewModel registerViewModel);
/// <summary>
/// Получение аутентификационных данных
/// </summary>
/// <param name="login">Логин</param>
/// <returns>Аутентификационные данные</returns>
ClaimsPrincipal GetClaims(string login);
}
}
\ No newline at end of file
using System;
using System.Security.Cryptography;
using System.Text;
namespace FileDesk.Services
namespace FileDesk.Services
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Security.Cryptography;
using System.Text;
using Microsoft.EntityFrameworkCore;
using FileDesk.DataAccess.Repository;
using FileDesk.Domain.Models;
using FileDesk.Domain.ViewModels;
/// <summary>
/// Сервис для работы с пользователями
/// </summary>
public class UserService
public class UserService : IUserService
{
private readonly FileDeskContext _db;
/// <summary>
/// Создает новый экземпляр класса <see cref = "UserService"/>
/// </summary>
/// <param name="db">Контекст для работы с БД</param>
public UserService(FileDeskContext db)
{
_db = db;
}
/// <summary>
/// Проверка существования пользователя
/// </summary>
/// <param name="login">Логин</param>
/// <returns>Существует ли пользователь</returns>
public bool IsExist(string login)
{
return _db.Users.FirstOrDefault(u => u.Login == login) != null;
}
/// <summary>
/// Проверка корректности ввода логина и пароля
/// </summary>
/// <param name="login">Логин</param>
/// <param name="password">Пароль</param>
/// <returns>Существует ли в БД комбинация логина и пароля</returns>
public bool IsCorrect(string login, string password)
{
return _db.Users.FirstOrDefault(u => (u.Login == login) && (CreateHash(password) == u.Password)) != null;
}
/// <summary>
/// Регистрация пользователя
/// </summary>
/// <param name="registerViewModel">Вью-модель для регистрации пользователя</param>
public void Register(RegisterViewModel registerViewModel)
{
_db.Users.Add(new User
{
Login = registerViewModel.Login,
Password = CreateHash(registerViewModel.Password),
UserRoles = new List<UserRole>
{
new UserRole
{
RoleId = 2 //Пользователь
}
}
});
_db.SaveChanges();
}
/// <summary>
/// Получение аутентификационных данных
/// </summary>
/// <param name="login">Логин</param>
/// <returns>Аутентификационные данные</returns>
public ClaimsPrincipal GetClaims(string login)
{
var user = _db.Users.Include(u => u.UserRoles).ThenInclude(u => u.Role).FirstOrDefault(u => u.Login == login);
var claims = new List<Claim>
{
new Claim("Id", user.Id.ToString()),
new Claim(ClaimsIdentity.DefaultNameClaimType, user.Login)
};
foreach (var role in user.UserRoles)
{
claims.Add(new Claim(ClaimsIdentity.DefaultRoleClaimType, role.Role.Name));
}
return new ClaimsPrincipal(new ClaimsIdentity(claims, "FileDeskCookies", ClaimsIdentity.DefaultNameClaimType, ClaimsIdentity.DefaultRoleClaimType));
}
/// <summary>
/// Получение хэша строки
/// </summary>
/// <param name="value">Входная строка</param>
/// <returns>Хэш</returns>
public static string CreateHash(string value)
private static string CreateHash(string value)
{
using var md5 = MD5.Create();
var inputBytes = Encoding.ASCII.GetBytes(value);
......@@ -29,16 +115,5 @@ namespace FileDesk.Services
return sb.ToString();
}
/// <summary>
/// Сравнение хэшей
/// </summary>
/// <param name="value">Значение</param>
/// <param name="hash">Хэш</param>
/// <returns>Хэш</returns>
public static bool ValidateHash(string value, string hash)
{
return 0 == StringComparer.OrdinalIgnoreCase.Compare(CreateHash(value), hash);
}
}
}
}
\ No newline at end of file
......@@ -7,6 +7,7 @@ using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using FileDesk.Web.Models;
using FileDesk.Services;
using Microsoft.AspNetCore.Authorization;
namespace FileDesk.Web.Controllers
{
......@@ -19,11 +20,18 @@ namespace FileDesk.Web.Controllers
_logger = logger;
}
[Authorize(Roles = "admin, user")]
public IActionResult Index()
{
return View();
}
public IActionResult AccessDenied()
{
return View();
}
[Authorize(Roles = "admin")]
public IActionResult Privacy()
{
return View();
......
namespace FileDesk.Web.Controllers
{
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Mvc;
using FileDesk.Domain.ViewModels;
using FileDesk.Services;
/// <summary>
/// Контроллер для работы с пользователями
/// </summary>
public class UserController : Controller
{
#region Свойства
private readonly IUserService _userService;
#endregion
#region Конструкторы и деструкторы
/// <summary>
/// Создает новый экземпляр класса <see cref = "UserController"/>
/// </summary>
/// <param name="userService">Сервис для работы с пользователями</param>
public UserController(IUserService userService)
{
_userService = userService;
}
#endregion
#region Методы
/// <summary>
/// Получение страницы регистрации
/// </summary>
/// <returns>Страница регистрации</returns>
[HttpGet]
public IActionResult Register()
{
return View();
}
/// <summary>
/// Регистрация пользователя
/// </summary>
/// <param name="model"></param>
/// <returns></returns>
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Register(RegisterViewModel model)
{
if (!_userService.IsExist(model.Login))
{
_userService.Register(model);
await Authenticate(model.Login);
return RedirectToAction("Index", "Home");
}
else
{
ModelState.AddModelError("", $"Пользователь с логином {model.Login} уже существует в БД");
}
return View(model);
}
/// <summary>
/// Получение страницы авторизации
/// </summary>
/// <returns>Страница авторизации</returns>
[HttpGet]
public IActionResult Login()
{
return View();
}
/// <summary>
/// Авторизация пользователя
/// </summary>
/// <returns></returns>
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Login(LoginViewModel model)
{
if (ModelState.IsValid)
{
if (_userService.IsCorrect(model.Login, model.Password))
{
await Authenticate(model.Login);
return RedirectToAction("Index", "Home");
}
ModelState.AddModelError("", "Некорректный логин и(или) пароль");
}
return View(model);
}
/// <summary>
/// Выход со страницы пользователя
/// </summary>
/// <returns></returns>
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Logout()
{
await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
return RedirectToAction("Login", "User");//new EmptyResult();
}
/// <summary>
/// Аутентификация пользователя
/// </summary>
/// <returns></returns>
private async Task Authenticate(string login)
{
await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, _userService.GetClaims(login));
}
#endregion
}
}
\ No newline at end of file
using System;
namespace FileDesk.Web.Models
{
public class ErrorViewModel
......@@ -8,4 +6,4 @@ namespace FileDesk.Web.Models
public bool ShowRequestId => !string.IsNullOrEmpty(RequestId);
}
}
}
\ No newline at end of file
namespace FileDesk.Web
{
using FileDesk.DataAccess.Repository;
using FileDesk.Services;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.EntityFrameworkCore;
......@@ -21,6 +23,16 @@ namespace FileDesk.Web
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<FileDeskContext>(options => options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie(options =>
{
options.LoginPath = new Microsoft.AspNetCore.Http.PathString("/User/Login");
options.AccessDeniedPath = new Microsoft.AspNetCore.Http.PathString("/Home/AccessDenied");
});
services.AddTransient<IUserService, UserService>();
services.AddControllersWithViews();
}
......@@ -42,6 +54,7 @@ namespace FileDesk.Web
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
......
Здесь будет контактная информация
\ No newline at end of file
Доступ к запрашиваемой странице запрещен. Требуются права администратора.
\ No newline at end of file
......@@ -3,8 +3,8 @@
ViewData["Title"] = "Error";
}
<h1 class="text-danger">Error.</h1>
<h2 class="text-danger">An error occurred while processing your request.</h2>
<h1 class="text-danger">Ошибка</h1>
<h2 class="text-danger">Во время обработки Вашего запроса произошла ошибка. Сообщите о ней <a asp-area="" asp-controller="Home" asp-action="Contacts">разработчику</a></h2>
@if (Model.ShowRequestId)
{
......@@ -22,4 +22,4 @@
It can result in displaying sensitive information from exceptions to end users.
For local debugging, enable the <strong>Development</strong> environment by setting the <strong>ASPNETCORE_ENVIRONMENT</strong> environment variable to <strong>Development</strong>
and restarting the app.
</p>
</p>
\ No newline at end of file
......@@ -24,6 +24,26 @@
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Privacy">Privacy</a>
</li>
@if (User.Identity.IsAuthenticated)
{
<li class="nav-item">
<p class="nav-link text-dark">@User.Identity.Name</p>
</li>
<li class="nav-item">
<form method="post" asp-controller="User" asp-action="Logout">
<input class="nav-link text-dark" type="submit" value="Выход" />
</form>
</li>
}
else
{
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="User" asp-action="Login">Вход</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="User" asp-action="Register">Регистрация</a>
</li>
}
</ul>
</div>
</div>
......
@model FileDesk.Domain.ViewModels.LoginViewModel
<h2>Вход на сайт</h2>
<a asp-action="Register" asp-controller="User">Регистрация</a>
<form asp-action="Login" asp-controller="User" asp-anti-forgery="true">
<div class="validation" asp-validation-summary="All"></div>
<div>
<div class="form-group">
<label asp-for="Login">Введите логин</label><br />
<input type="text" asp-for="Login" />
<span asp-validation-for="Login" />
</div>
<div class="form-group">
<label asp-for="Password">Введите пароль</label><br />
<input asp-for="Password" />
<span asp-validation-for="Password" />
</div>
<div class="form-group">
<input type="submit" value="Войти" class="btn" />
</div>
</div>
</form>
\ No newline at end of file
@model FileDesk.Domain.ViewModels.RegisterViewModel
<h2>Регистрация</h2>
<form asp-action="Register" asp-controller="User" asp-anti-forgery="true">
<div class="validation" asp-validation-summary="All"></div>
<div>
<div class="form-group">
<label asp-for="Login">Введите логин</label><br />
<input type="text" asp-for="Login" />
<span asp-validation-for="Login" />
</div>
<div class="form-group">
<label asp-for="Password">Введите пароль</label><br />
<input asp-for="Password" />
<span asp-validation-for="Password" />
</div>
<div class="form-group">
<label asp-for="ConfirmPassword">Повторите пароль</label><br />
<input asp-for="ConfirmPassword" />
<span asp-validation-for="ConfirmPassword" />
</div>
<div class="form-group">
<input type="submit" value="Регистрация" class="btn" />
</div>
</div>
</form>
\ No newline at end of file
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment