Как аутентифицировать пользователей в вашем приложении Node с помощью файлов cookie и Passport.js

Asp.net core | аутентификация на основе куки

Последнее обновление: 13.12.2022

Теперь создадим простенькую инфраструктуру для авторизации и регистрации пользователя. Сначала добавим в наш проект вспомогательные модели,
которые помогут выполнить логин и регистрацию пользователя. Для этого добавим в проект папку ViewModels. В нее добавим класс RegisterModel,
который будет представлять модель регистрации:

using System.ComponentModel.DataAnnotations;

namespace AuthApp.ViewModels
{
    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; }
    }
}

В ней определено три свойства: Email, пароль и подтверждение пароля. Также во ViewModels добавим класс LoginModel, который будет представлять модель
логина:

using System.ComponentModel.DataAnnotations;

namespace AuthApp.ViewModels
{
    public class LoginModel
    {
        [Required(ErrorMessage = "Не указан Email")]
        public string Email { get; set; }
		
        [Required(ErrorMessage = "Не указан пароль")]
        [DataType(DataType.Password)]
        public string Password { get; set; }
    }
}

Затем добавим в папку Controllers новый контроллер, который назовем AccountController:

using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Microsoft.AspNetCore.Mvc;
using System.Security.Claims;
using AuthApp.ViewModels; // пространство имен моделей RegisterModel и LoginModel
using AuthApp.Models; // пространство имен UserContext и класса User
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;

namespace AuthApp.Controllers
{
    public class AccountController : Controller
    {
        private UserContext db;
        public AccountController(UserContext context)
        {
            db = context;
        }
        [HttpGet]
        public IActionResult Login()
        {
            return View();
        }
        [HttpPost]
        [ValidateAntiForgeryToken]
        public async Task<IActionResult> Login(LoginModel model)
        {
            if (ModelState.IsValid)
            {
                User user = await db.Users.FirstOrDefaultAsync(u => u.Email == model.Email && u.Password == model.Password);
                if (user != null)
                {
                    await Authenticate(model.Email); // аутентификация

                    return RedirectToAction("Index", "Home");
                }
                ModelState.AddModelError("", "Некорректные логин и(или) пароль");
            }
            return View(model);
        }
        [HttpGet]
        public IActionResult Register()
        {
            return View();
        }
        [HttpPost]
        [ValidateAntiForgeryToken]
        public async Task<IActionResult> Register(RegisterModel model)
        {
            if (ModelState.IsValid)
            {
                User user = await db.Users.FirstOrDefaultAsync(u => u.Email == model.Email);
                if (user == null)
                {
                    // добавляем пользователя в бд
                    db.Users.Add(new User { Email = model.Email, Password = model.Password });
                    await db.SaveChangesAsync();

                    await Authenticate(model.Email); // аутентификация

                    return RedirectToAction("Index", "Home");
                }
                else
                    ModelState.AddModelError("", "Некорректные логин и(или) пароль");
            }
            return View(model);
        }

        private async Task Authenticate(string userName)
        {
            // создаем один claim
            var claims = new List<Claim>
            {
                new Claim(ClaimsIdentity.DefaultNameClaimType, userName)
            };
            // создаем объект ClaimsIdentity
            ClaimsIdentity id = new ClaimsIdentity(claims, "ApplicationCookie", ClaimsIdentity.DefaultNameClaimType, ClaimsIdentity.DefaultRoleClaimType);
            // установка аутентификационных куки
            await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, new ClaimsPrincipal(id));
        }

        public async Task<IActionResult> Logout()
        {
            await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
            return RedirectToAction("Login", "Account");
        }
    }
}

Так как в файле Startup ранее были добавлены сервисы Entity Framework, то мы можем получить объект контекста данных в конструкторе контроллера.

Похожее:  Личный кабинет Билайн - регистрация, вход, управление услугами - Мобильный Билайн – Поддержка – Билайн Махачкала

Для входа на сайт определена пара методов Login. Get-версия метода просто возвращает представление с формой, которые мы далее создадим. Post-версия принимает
в качестве параметра модель LoginModel. Вначале смотрим, а есть ли с таким же email в базе данных какой-либо пользователь, если такой пользователь имеется в БД, то выполняем
аутентификацию и устанавливаем аутентификационные куки. Чтобы не повторяться (в соответствии с принципом DRY), данный код вынесен в отдельный метод Authenticate:

private async Task Authenticate(string userName)
{
	// создаем один claim
	var claims = new List<Claim>
	{
		new Claim(ClaimsIdentity.DefaultNameClaimType, userName)
	};
	// создаем объект ClaimsIdentity
	ClaimsIdentity id = new ClaimsIdentity(claims, "ApplicationCookie", ClaimsIdentity.DefaultNameClaimType, ClaimsIdentity.DefaultRoleClaimType);
	// установка аутентификационных куки
	await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, new ClaimsPrincipal(id));
}

Для установки кук применяется асинхронный метод контекста HttpContext.SignInAsync(). В качестве параметра он принимает
схему аутентификации, которая была использована при вызове метода services.AddAuthentication() в методе ConfigureServices() в классе Startup. То есть
в нашем случае это CookieAuthenticationDefaults.AuthenticationScheme. А в качестве второго параметра передается объект ClaimsPrincipal, который представляет пользователя.

Для правильного создания и настройки объекта ClaimsPrincipal вначале создается список claims – набор данных, которые шифруются и добавляются в аутентификационные куки.
Каждый такой claim принимает тип и значение. В нашем случае у нас только один claim, который в качестве типа принимает константу ClaimsIdentity.DefaultNameClaimType,
а в качестве значения – email пользователя.

Далее создается объект ClaimsIdentity, который нужен для инициализации ClaimsPrincipal. В ClaimsIdentity передается:

  • Ранее созданный список claims

  • Тип аутентификации, в данном случае “ApplicationCookie”

  • Тип данных в списке claims, который преставляет логин пользователя. То есть при добавлении claimа мы использовали в качестве типа
    ClaimsIdentity.DefaultNameClaimType, поэтому и тут нам надо указать то же самое значение. Мы, конечно, можем указать и разные значения, но тогда
    система не сможет связать различные claim с логином пользователя.

  • Тип данных в списке claims, который представляет роль пользователя. Хотя у нас такого claim нет, который бы представлял роль пользователя, но
    но опционально мы можем указать константу ClaimsIdentity.DefaultRoleClaimType. В данном случае она ни на что не влияет.

И после вызова метода расширения HttpContext.SignInAsync в ответ клиенту будут отправляться аутентификационные куки, которые при последующих
запросах будут передаваться обратно на сервер, десериализоваться и использоваться для аутентификации пользователя.

Метод Register сделан аналогично методу Login, только теперь мы получаем данные регистрации через объект RegisterModel и перед аутентификацией
сохраняем эти данные в базу данных.

Для выхода из сайта определен метод Logout, суть которого в вызове метода

HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);

В этот метод опять же передается название схемы аутентификации, использованное в классе Startup.

И после создания контроллера остается добавить нужные представления. Для этого в проекте в папке Views создадим подкаталог Account. И в него добавим представление
Login.cshtml:

@model AuthApp.ViewModels.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 btn-outline-dark" />
        </div>
    </div>
</form>

И также добавим представление для регистрации Register.cshtml:

@model AuthApp.ViewModels.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>

Для тестирования системы аутентификации изменим метод Index в контроллере HomeController, который имеется по умолчанию:

using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Authorization;

namespace AuthApp.Controllers
{
    public class HomeController : Controller
    {
        [Authorize]
        public IActionResult Index()
        {
            return Content(User.Identity.Name);
        }
    }
}

Атрибут Authorize предотвращает неаутентифицированный доступ к методу Index. Если анонимный пользователь попытается обратиться к этому методу,
то его перенаправит по пути Account/Login, то есть на метод логина. Если же пользователь аутентифицирован, то он увидит в браузере свой логин, который можно получить через свойство
User.Idenity.Name.

В итоге весь проект будет выглядеть следующим образом:

Аутентификация в ASP.NET Core 2.0

Теперь, когда все готово, протестируем приложение. При его запуске автоматически идет обращение по маршруту Home/Index, но так как мы неаутентифицированы,
то нас перенаправляет на метод логина:

Логин в ASP.NET Core

Естественно, что у нас пока нет никаких пользователей в БД и залогиниться мы не можем. Поэтому перейдем по ссылке регистрации и введем в регистрационную форму
какие-нибудь данные:

Регистрация в ASP.NET Core

И после нажатия на кнопку регистрации, данные пользователя будут добавлены в БД, произойдет аутентификация, и пользователя перенаправит на метод Index,
который выведет его логин:

Авторизация в ASP.NET Core

Кроме того, в самом браузере мы сможем увидеть сохраненные аутентификационные куки, которые называются .AspNetCore.Cookies или .AspNetCore.[Название схемы аутентификации].
(Вторые куки – это куки для отслеживания antiforgery-токена, который используется для валидации запросов к приложению)

Таким образом, мы можем добавить простейшую систему аутентификации и авторизации в приложение ASP.NET Core

НазадСодержаниеВперед

How does cookie based authentication work?

To expand on Conor’s answer and add a little bit more to the discussion…

Can someone give me a step by step description of how cookie based authentication works? I’ve never done anything involving either authentication or cookies. What does the browser need to do? What does the server need to do? In what order? How do we keep things secure?

Step 1: Client > Signing up

Before anything else, the user has to sign up. The client posts a HTTP request to the server containing his/her username and password.

Step 2: Server > Handling sign up

The server receives this request and hashes the password before storing the username and password in your database. This way, if someone gains access to your database they won’t see your users’ actual passwords.

Step 3: Client > User login

Now your user logs in. He/she provides their username/password and again, this is posted as a HTTP request to the server.

Step 4: Server > Validating login

The server looks up the username in the database, hashes the supplied login password, and compares it to the previously hashed password in the database. If it doesn’t check out, we may deny them access by sending a 401 status code and ending the request.

Step 5: Server > Generating access token

If everything checks out, we’re going to create an access token, which uniquely identifies the user’s session. Still in the server, we do two things with the access token:

  1. Store it in the database associated with that user
  2. Attach it to a response cookie to be returned to the client. Be sure to set an expiration date/time to limit the user’s session

Henceforth, the cookies will be attached to every request (and response) made between the client and server.

Step 6: Client > Making page requests

Back on the client side, we are now logged in. Every time the client makes a request for a page that requires authorization (i.e. they need to be logged in), the server obtains the access token from the cookie and checks it against the one in the database associated with that user. If it checks out, access is granted.

This should get you started. Be sure to clear the cookies upon logout!

Как аутентифицировать пользователей с помощью промежуточного программного обеспечения passport.js

До сих пор мы видели, как аутентифицировать пользователей с помощью файлов cookie и сеансов. Теперь мы увидим третий метод аутентификации.

Passport.js – это промежуточное ПО для аутентификации в Node, которое позволяет аутентифицировать пользователей с помощью сеансов и OAuth. Он также позволяет создавать собственные стратегии и многое другое.

Этот код устанавливает все необходимые модули для определения подходящей локальной стратегии. Стратегия passport-local позволяет аутентификацию с помощью имени пользователя и пароля.

Как аутентифицировать пользователя с помощью сеансов

Давайте рассмотрим аналогию, чтобы помочь нам понять сеансы по сравнению с файлами cookie. Представьте, что вы рассеянный человек, который все время забывает имена своих друзей.

Одно из решений – дать каждому другу карточку с его именем и изображением. Каждый раз, когда вы встречаетесь с ними, просто просите показать карточку, которую вы им дали, чтобы освежить свою память.

Проблема в том, что ваши друзья могут потерять эту карту. Или двое из них могут поменяться картами и подшутить над вами. Или, может быть, у вашего друга недостаточно места для хранения другой карты.

В любом случае механизм аутентификации обнаруживает признаки слабости. Но это, по сути, то, что делают файлы cookie – они хранятся на стороне клиента, и каждый раз, когда клиент делает запрос на свой сайт, cookie используется для аутентификации.

Предположим, вы делаете карточки для каждого друга и храните их при себе. Когда вы их видите, вы назначаете способ сопоставления карты с человеком для облегчения идентификации. Таким образом, информация не хранится у клиента и, следовательно, более безопасна.

Этот сценарий показывает нам, как работают сеансы. Информация о новом аутентифицированном пользователе находится на сервере, и только минимальная информация передается обратно клиенту. Таким образом, клиент может быть сопоставлен с сохраненной частью информации.

express-session создает промежуточное ПО для сеансов, которое позволяет легко настраивать сеансы и управлять ими.

Серверное хранилище по умолчанию – это MemoryStore. Чтобы хранить информацию о сеансе в виде файлов JSON, вам потребуется хранилище файлов сеанса. Приведенный ниже код делает следующее:

  1. Устанавливает приложение Express
  2. Он сообщает промежуточному программному обеспечению запрашивать аутентификацию, если ничего не указано, и в противном случае проверяет, совпадают ли имя пользователя и пароль.
  3. Если нет, ему снова нужно сделать тот же запрос на аутентификацию, иначе сеанс будет установлен.
  4. Затем он добавляет имя пользователя в качестве атрибута пользователя и впоследствии проверяет его.

Опять же, это всего лишь простой пример – информация, которую необходимо проверить на соответствие заданным данным, должна как минимум храниться в базе данных.

let app=express()
app.use(session({
  store: new File_Store,
  secret: 'hello world',
  resave: true,
  saveUninitialized: true
}))
app.use('/', (req,res,next)=>{
  if(!req.session.user)
  {
    console.log("Session not set-up yet")
    if(!req.headers.authorization)
    {
      console.log("No auth headers")
      res.setHeader("WWW-Authenticate", "Basic")
      res.sendStatus(401)
    }
    else
    {
      auth_stuff=new Buffer.from(req.headers.authorization.split(" ")[1], 'base64')
      step1=auth_stuff.toString().split(":")
      console.log("Step1: ", step1)
      if(step1[0]=='admin' && step1[1]=='admin')
      {
        console.log('GENUINE USER')
        req.session.user='admin'
        res.send("GENUINE USER")
      }
      else
      {
        res.setHeader("WWW-Authenticate", "Basic")
        res.sendStatus(401)
      }
    }
  }

Как аутентифицировать пользователя с помощью файлов cookie

Прежде всего, давайте создадим простое приложение, которое аутентифицирует пользователей с использованием классического метода имени пользователя и пароля – сейчас мы не будем беспокоиться о подключениях к базе данных.

Каждый раз, когда мы пытаемся загрузить страницу, браузер отправляет запрос на сервер, который отвечает соответствующим образом.

Первоначально, когда пользователь заходит на сайт, ему предлагается ввести зарегистрированное имя пользователя и пароль. После передачи они сообщаются серверу, поэтому последующие запросы к серверу не требуют повторного подтверждения их личности.

Но как сервер будет отслеживать, какие пользователи уже аутентифицированы, а какие нет? Вот тут и пригодятся куки.

Согласно w3.org, файлы cookie определяются как:

Регистрация и авторизация с помощью cookie

Начал освежать знания по php. Просматривая видеуроки на http://www.vhod-v-lichnyj-kabinet.ru/playlist?list=PLYZm5HGThGRBZ3ym2Ek0h4jQvYAxuPLBe я узнал, что используя сессии для регистрации и авторизации пользователей, при закрытии браузера и открытии его снова теряется факт входа в аккаунт пользователя, поэтому в этих видеоуроках было сказано, что необходимо использовать cookies (т. к. они сохраняются на жестком диске пользователя). Просмотрев видеоуроки на http://www.vhod-v-lichnyj-kabinet.ru/watch?v=qFxUApU6104&list=PLYZm5HGThGRBZ3ym2Ek0h4jQvYAxuPLBe , http://www.vhod-v-lichnyj-kabinet.ru/watch?v=ASdD2gasVsA&list=PLYZm5HGThGRBZ3ym2Ek0h4jQvYAxuPLBe , http://www.vhod-v-lichnyj-kabinet.ru/watch?v=Nyk60llAcMc&list=PLYZm5HGThGRBZ3ym2Ek0h4jQvYAxuPLBe , http://www.vhod-v-lichnyj-kabinet.ru/watch?v=ysmj-iQGkII&list=PLYZm5HGThGRBZ3ym2Ek0h4jQvYAxuPLBe я попытался написать регистрацию и авторизацию с использованием cookies, код которых приводится ниже. Я думал, но не удалось достичь фиксации входа в аккаунт при закрытии и открытии браузера. Помогите написать, используя сookies, с учетом, что их может существовать несколько для одного пользователя в случае, если человек по каким-то причинам решил создать несколько аккаунтов для одного сайта, регистрацию и авторизацию c сохранением входа на сайт. Привожу код

<?php

    $connect = mysql_connect('localhost', 'root', '') or die(mysql_error());
    mysql_select_db('tutorials');

    if (isset($_POST['submit'])){
        $username = $_POST['username'];
        $login = $_POST['login'];
        $password = $_POST['password'];
        $r_password = $_POST['r_password'];

        if ($password == $r_password){
            $password = md5($password);
            $query = mysql_query("INSERT INTO users VALUES ('', '$username', '$login', '$password')");
            //setcookie($login, $password, time()   3600*24*7, "/");

        }
        else
        {
            die('Passwords not match');
        }
    }

    if (isset($_POST['enter'])){
        //echo "OK";
        $e_login = $_POST['e_login'];
        $e_password = md5($_POST['e_password']);

        $query = mysql_query("SELECT * FROM users WHERE login='$e_login'");
        $user_data = mysql_fetch_array($query);

        if ($user_data['password'] == $e_password) {
            setcookie($e_login, $e_password, time()   3600*24*7, "/");
            echo '<form method="post" action="register.php">
                    <input type="text" name="username" placeholder="| Username" required /><br>
                    <input type="text" name="login" placeholder="| Login" required /><br>
                    <input type="password" name="password" placeholder="| Password" required /><br>
                    <input type="password" name="r_password" placeholder="| Repeat password" required /><br>
                    <input type="submit" name="submit" value="Register" /><br>
                    </form>';
            echo 'You have already logged in<br>
                 <form method="post" action="register.php">
                 <input type="submit" name="logout" value="Logout" />
                </form>';           
        }
        else
        {
            echo "Wrong password or login";
        }
    }
    else
    {
        setcookie($e_login, $e_password, time() - 3600*24*7, "/");
        echo '<form method="post" action="register.php">
                <input type="text" name="username"  placeholder="| Username" required /><br>
                <input type="text" name="login" placeholder="| Login" required /><br>
                <input type="password" name="password" placeholder="| Password" required /><br>
                <input type="password" name="r_password" placeholder="| Repeat password" required /><br>
                <input type="submit" name="submit" value="Register" /><br>
                </form>';

        echo '<form method="post" action="register.php">
              <input type="text" name="e_login" placeholder="| Entered login" required /><br>
              <input type="password" name="e_password" placeholder="| Entered password" required /><br>
              <input type="submit" name="enter" value="Enter" /><br>
              </form>';
    }

?>

Заключение

На этом я завершаю обзор различных методов аутентификации в Node.

Используемый здесь код далек от идеала, но я надеюсь, что он поможет кому-то, кто только начал изучать аутентификацию и чувствовал себя подавленным.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *