Регистрация и авторизация с помощью Spring Security на примере простого приложения / Хабр

Описание основных используемых аннотаций

Controller

Что такое сессии?

Попробую рассказать в паре абзацев.

Сессии – это механизм, который позволяет однозначно идентифицировать пользователя.
По-человечески это значит, что для каждого посетителя сайта можно создать уникальное “хранилище”, к которому будет доступ только у этого самого посетителя.
Хранилище это хранится в файле на сервере.

На самом деле сессии хранятся не обязательно в файлах. Например, cms Modx хранит их базе данных.
Но сейчас нам это не важно, главное, что сессии предоставляют удобный способ работать с данными, уникальными для пользователя.
Для нас сессия выглядит как обычный ассоциативный массив под названием $_SESSION, в разные поля которого можно записать хоть число, хоть строку, хоть сериализованный объект.

Что делать дальше?


И вот мы подбираемся к самому интересному: а где хранить пароли и в каком виде? Давайте сначала подумаем, где их хранить.

Что будет представлять из себя приложение

Сайт со следующими страницам:

Создание нового проекта в IDE

Мы будем использовать систему сборки Maven.

Под GroupId подразумевается уникальный идентификатор компании (или ваше личное доменное имя), которая выпускает проект. ArtefactId – это просто название нашего проекта.

После завершения создания проекта отроется файл pom.xml, Idea предложит включить автоимпорт – не отказывайтесь. В этом файле будут содержаться все зависимости (библиотеки), используемые в проекте.

Создание структуры проекта (пакетов)

Сразу перейдем к созданию пакетов. Структура проекта, которая должна получиться показана ниже.

Теперь коротко о том, что будет храниться в каждом пакете:

Рассмотрим файл

pom.xml

. В этом файле нужно указать ссылку на родительский файл с помощью тега

parent

, т.о. все свойства и зависимости родителя будут добавлены в этот дочерний файл.


Далее добавляем зависимости для работы модулей Spring, драйвер БД PostgreSQL, сервера Tomcat, JSTL.

По умолчанию maven будет использовать старую версию java 1.6, чтобы это исправить указываем версию явно.

Также добавляем плагин, позволяющий упаковывать архивы jar или war и запускать их «на месте»:


Заполним файл

application.properties

1. Добавление сущностей (моделей)

Обязательное требование для всех сущностей: приватные поля, геттеры и сеттеры для всех полей и пустой конструктор (в примерах не представлены). Их не нужно писать вручную, нажмите Alt Insert и Idea сделает это за вас.

Для импорта необходимых классов и библиотек используем комбинацию клавиш Alt Enter.

Регистрация и авторизация с помощью Spring Security на примере простого приложения / Хабр

2. Реализация слоя доступа к данным и сервисного слоя

Spring Data предоставляет набор готовых реализаций для создания слоя, обеспечивающего доступ к БД. Интерфейс

JpaRepository

предоставляет набор стандартных методов (findBy, save, deleteById и др.) для работы с БД.

3. Добавление контроллеров


Для страниц, которые никак не обрабатываются сервером, а просто возвращают страницу, маппинг можно настроить в конфигурации. Страница

login

обрабатывается Spring Security контроллером по умолчанию, поэтому для неё отдельный контроллер не требуется.

@Configuration
public class MvcConfig implements WebMvcConfigurer {

    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/login").setViewName("login");
        registry.addViewController("/news").setViewName("news");
    }
}

RegistrationController.

Отдельный контроллер нужен для страницы регистрации. Для обработки GET запроса используется аннотация

@GetMapping(“/registration”)

, для POST –

@PostMapping(“/registration”)


Чтобы что-то добавить или получить со страницы мы обращаемся к model. В GET запросе на страницу добавляется новый пустой объект класса

4. Добавление представлений

index.jsp

Запуск приложения

В main класс Application нужно добавить следующее:

Выбираем правильное хэширование

Идею хранения паролей нашли, то есть хранения не паролей, а их хэшей. А вот какой алгоритм хэширования выбрать?


Давайте посмотрим на то, что пробовали выше – простая функция md5. Алгоритма его расшифровки нет, но тем не менее md5 не рекомендуется для использования. Почему?

Помимо сложности самого алгоритма, есть и другой момент. Да, расшифровать пароль по хэшу нельзя, но его можно подобрать. Простым брутфорсом.
К тому же существуют многочисленные базы паролей md5, где всевозможные варианты хранятся тупо списком.
И подбор вашего пароля по известному хэшу займет столько времени, сколько понадобится для выполнения sql-запроса

    select password from passwords where hash='e10adc3949ba59abbe56e057f20f883e';

Конечно, вы сами не будете использовать пароль 123456, но как насчет ваших пользователей? Да, 123456 можно подобрать и руками, но в таком случае никакие алгоритмы не помогут.
Наша же задача максимально позаботиться о юзерах, которые выбирают пароли сложнее qwerty. Думаем дальше, гуглим.

Помимо md5 есть множество алгоритмов хэширования, sha256, sha512 и еще целая толпа. Их сложность выше, но это не отменяет опять-таки существования таблиц с готовыми паролями.
Нужно что-то хитрее.

Заглушка для авторизации

Функции, связанные с авторизацией, будут лежать в отдельном файле и своем пространстве имен. Создадим файл auth.php в api/v1/common – там, где уже лежит helpers.php.
Если вы разбирали уроки админки, особенно третий, про серверную часть, то эти пути вам будут знакомы. Если у вас свой проект, то кладите auth.php куда удобно.
Главное, потом правильно указать пути.

Содержимое auth.php

Используемые источники

  1. Registration and Login with Spring Boot, Spring Security, Spring Data JPA, Hibernate, MySQL, JSP, Bootstrap and Docker Compose
  2. Обратная сторона Spring
  3. Spring Security/Технический обзор Spring Security
  4. Официальная документация Spring
  5. Учимся готовить: Spring 3 MVC Spring Security Hibernate

Как обойтись без шифрования. хэширование

Фокус в том, что не нужно хранить пароли в открытом виде, но и не нужно шифровать их с возможностью расшифровки. Пароли нужно хэшировать и в базе хранить не пароль, а его хэш.
Хитрым образом закодированную строку, которую нельзя расшифровать. Например, не password, а 5f4dcc3b5aa765d61d8327deb882cf99

Вы можете спросить, что это за хрень и как же сравнить пароль, введенный пользователем, с паролем, лежащим в базе. А нам и не нужно сравнивать пароли – достаточно сравнить их хэши.


Например, есть простая функция хэширования md5. Вот так она работает

Как сделать авторизацию на php? пишем авторизацию пользователя | otus

PHP_Deep_1.4-5020-7f0114.png

Внимание! Данная статья является устаревшей и носит исключительно ознакомительный характер. Если вас интересует авторизация на PHP, рекомендуем прочитать следующий материал по использованию готовых библиотек авторизации.

В этой статье вы узнаете, как сделать PHP-авторизацию (authentication) на сайте с помощью данных, полученных от пользователя при регистрации. Будем использовать таблицу MySQL, а сама PHP-авторизация будет работать с применением сессий и cookie. Материал не следует рассматривать, как пошаговое руководство. Зато он помогает понять, как работает PHP-авторизация в целом.

В первую очередь нужно сверстать главную страницу веб-сайта, поместив её в корне в папку template. Для этого создаём файл index.html с формой ввода логина и пароля, кнопкой «Вход», вот её код:

Мы используем метод передачи post, который необходим. Нам ведь не нужно, чтобы в процессе PHP-авторизации пароль и логин «светились» в адресной строке.

Когда форма готова, создаём главный контроллер — наиболее важный файл сайта, лежащий в корне — index.php. Как раз он и будет запускаться во время входа. Код выглядит следующим образом:

Теперь разберёмся подробнее, как всё работает.

В первых 3 строках просто подключаются файлы с функциями, необходимыми для дальнейшего использования в коде. О них поговорим потом. Далее проверяем, был ли передан get-параметрaction=out. В случае его передачи пользователь нажал на ссылку выхода с веб-сайта. Код ссылки, который нужно добавить в файл, содержащий код формы для входа:

Потом у нас идёт условие, которое проверяет, авторизован ли ты (if (login())). То есть функция возвращает нам true, если юзер вошёл на сайт. В противном случае возвращается false. Когда вернулось true, в переменную $UID записывается id юзера. Что касается переменной $admin, то в неё пишется результат работы функции is_admin($UID). Она определяет, является ли наш юзер админом, возвращая true, если это так и false в обратном случае. Потом эти 2 переменные понадобятся, чтобы вывести определённые элементы на странице.

Итак, форму PHP-авторизации можно вывести следующим условием:

Аналогичная ситуация и с переменной $admin. Последний код тоже можно поместить в файл с формой.

Если функция login() вернёт false (юзер не вошёл на сайт), мы проверим, а нажал ли он вообще на кнопку входа на сайт, которая включена в нашу форму PHP-авторизации:

Если это так, запускается функция enter(), авторизующая пользователя. Если ошибки отсутствуют, а пользователь вошёл успешно, создаём те же две переменные: $UID и $admin. В обратном случае переменные не создаются никакие, ведь пользователь является гостем.

Давайте посмотрим на алгоритм работы:

1-20219-6acf0c.jpg

А теперь рассмотрим все функции, вызываемые в коде. Вот функция входа на сайт:

Сначала функция проверит, а заполнено ли поле для ввода пароля и логина для PHP-аутентификации. Если да, работа программы продолжается, в противном случае в массив $error пишется текст ошибки, и происходит возвращение в основную программу, которая после того, как узнает размерность полученного массива, пользователя не авторизует.

Но если работа функции enter() будет продолжена, мы проверим, а существует ли в базе данных запись с таким ником. Когда записи нет, возвращается массив с ошибкой. Когда есть, введённый пароль сравнивается со значением, которое хранится в БД.

Пароли сравниваются не в чистом виде, т. к. в базе данных они хэшированы функцией md5(). Значит это следующее: прежде чем приступить к сравнению, нужно хэшировать тем же алгоритмом пароль, введённый пользователем при попытке пройти аутентификацию/авторизацию.

Когда хэши совпадают, происходит авторизация с помощью скрипта. При отсутствии совпадений, возвращается ошибка.

Давайте подробно остановимся на том, что значит «авторизироваться». В нашем скрипте информация о PHP-авторизации хранится в cookie и сессии. В сессию записывается id пользователя:

Кроме того, создаются 2 cookie: login и password. Продолжительность жизни — 50 тыс. секунд. В первый пишется логин, во 2-й — хэш пароля.

В данной строке выполняется функция, которая устанавливает время последней активности юзера. Код:

С помощью функции перезаписываются поля online и last_act в базе данных. Здесь, кстати, важно убедиться, что эти поля существуют (оба имеют тип int).

Теперь посмотрим на алгоритм работы функции enter():

2-20219-e35f69.jpg

Идём дальше. Следующая функция нужна для проверки, авторизирован ли юзер на сайте — login().

functionlogin(){ini_set("session.use_trans_sid",true);session_start();if(isset($_SESSION['id']))//если сесcия есть   {if(isset($_COOKIE['login'])&&isset($_COOKIE['password']))//если cookie есть, обновляется время их жизни и возвращается true      {           SetCookie("login","",time()-1,'/');SetCookie("password","",time()-1,'/');setcookie("login",$_COOKIE['login'],time() 50000,'/');setcookie("password",$_COOKIE['password'],time() 50000,'/');$id=$_SESSION['id'];lastAct($id);returntrue;}else//иначе добавляются cookie с логином и паролем, чтобы после перезапуска браузера сессия не слетала         {$rez=mysql_query("SELECT * FROM users WHERE id='{$_SESSION['id']}'");//запрашивается строка с искомым id             if(mysql_num_rows($rez)==1)//если получена одна строка          {       $row=mysql_fetch_assoc($rez);//она записывается в ассоциативный массив               setcookie("login",$row['login'],time() 50000,'/');setcookie("password",md5($row['login'].$row['password']),time() 50000,'/');$id=$_SESSION['id'];lastAct($id);returntrue;}elsereturnfalse;}}else//если сессии нет, проверяется существование cookie. Если они существуют, проверяется их валидность по базе данных     {if(isset($_COOKIE['login'])&&isset($_COOKIE['password']))//если куки существуют      {$rez=mysql_query("SELECT * FROM users WHERE login='{$_COOKIE['login']}'");//запрашивается строка с искомым логином и паролем             @$row=mysql_fetch_assoc($rez);if(@mysql_num_rows($rez)==1&&md5($row['login'].$row['password'])==$_COOKIE['password'])//если логин и пароль нашлись в базе данных           {$_SESSION['id']=$row['id'];//записываем в сесиию id              $id=$_SESSION['id'];lastAct($id);returntrue;}else//если данные из cookie не подошли, эти куки удаляются             {SetCookie("login","",time()-360000,'/');SetCookie("password","",time()-360000,'/');returnfalse;}}else//если куки не существуют      {returnfalse;}}}

Возникает вопрос, почему для авторизации используем и сессию, и cookie? Всё дело в том, что при закрытии браузера сессия «умирает», а пользователь автоматически разлогинивается. А вот cookie хранятся определённое время, задаваемое нами (50 тыс. секунд).

Дальше у нас функция online(). Её нужно запускать первой на всех модулях и страницах будущего сайта. В первую очередь, она проверяет, прошёл ли пользователь PHP-авторизацию/аутентификацию, что важно для дальнейшей работы скрипта. Во вторую очередь, она выполняет обновление времени последней активности, а также помогает в перспективе выводить систему онлайн-пользователей.

Наша функция вернёт true, когда юзер авторизирован, а в обратном случае — false. При этом в процессе работы обновится время жизни cookie или они будут созданы, если вообще не существуют.

Очередной алгоритм работы:

3-20219-7b542b.jpg

Когда есть сессия и cookie, обновляется время жизни cookie. Чтобы это реализовать, они удаляются, время смерти устанавливается на одну секунду раньше текущего времени, потом устанавливаете заново. Также нужно учесть, что функция lastAct() обновляет время последней активности. Возвращается, разумеется, true.

Когда сессия есть, а cookie почему то нет, то по id юзера мы получаем из базы данных логин и хэш пароля, потом пишем их в cookie. Возвращается true.

Когда сессии нет, проверяем, существуют ли cookie. Традиционный пример — PHP-авторизация после перезапуска браузера, когда сессия слетела, но cookie всё ещё живы. Становится сложнее, ведь нужно проверить, а совпадает ли пара пароль-логин с какой-нибудь строкой из базы данных. Ведь пользователь легко мог сменить cookie в настройках для сайта. Если пара нашлась, создаётся переменная сессии, возвращается true. Если пара не нашлась, возвращается false.

Если у нас самый печальный вариант — ни сессии, ни cookie не оказалось, возвращается false.

Сейчас глянем на функцию is_admin($UID), определяющую является ли user админом. Если вам это не надо, опустите данную функцию и её вызовы в контроллере. Но учтите, что для вывода контента на страницу для админов она бывает полезна. К тому же, она проста и основана на одном столбце в базе данных в таблице users (столбец prava, тип int).

Когда наш юзер обыкновенный пользователь, значению в столбце присваивается 0, иначе — 1. Соответственно, в первом случае вернётся false, во втором — true.

Последняя функция — это out(). Она проста и удаляет все «следы» юзера — и сессию, и cookie.

Код всех наших функций нужно поместить в файл lib/module_global.php, подключаемый в начале работы контроллера.

Итак, мы написали функциональную, но простую PHP-регистрацию/аутентификацию/авторизацию для сайта. Также заложили фундамент для дальнейших возможностей по администрированию и не только. Такая PHP-авторизация не слетит после перезапуска браузера, т. к. мы используем cookie. Предусмотрен и выход с сайта.

Источник: https://true-coder.ru/php/pishem-avtorizaciyu-na-php.html.

Немного информации о spring security

Самым фундаментальным объектом является

SecurityContextHolder

. В нем хранится информация о текущем контексте безопасности приложения, который включает в себя подробную информацию о пользователе (принципале), работающим с приложением. Spring Security использует объект

Authentication

, пользователя авторизованной сессии.

Описываем переключатели


Переключатели сделаем при помощи label, в нутрии располагаем заголовок (Вкладка 1, Вкладка 2) и соответственно названия полей.

Описываем структуры для авторизации

Открываем, тег form, присваиваем для него класс tab-form, что бы к ней было проще обращаться при оформлении.

В нутрии формы вкладываем input для ввода Email, прописываем название данного поля при помощи placeholder.

Дублированием текущий input, и модифицируем его под ввод пароля.


Ниже располагаем ссылку, которая будет кнопкой для отправки формы.

Ниже создаем блок с социальными иконками.

Блок с иконками оформим в виде списков, в нутрии каждого списка помещаем ссылку, а в ней уже размещаем иконку.


Иконки отбираю через статью Работа со шрифтовыми иконками. Вы так же можете перейти по ссылке, отобрать те иконки, которые вам нужны, и прописать соответствующий класс в теге (i) внутри ссылки.

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

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


Далее, ниже блока с иконками, размещаем ссылку на восстановление пароля.

Описываем структуры для регистрации

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

Первое поле так и остается для ввода Email, хотя можно дописать в нем placeholder  «Введите E-mail адрес», что бы чем-то оно отличалось.

Далее пойдет аналогичное поле для ввода имени только с другим атрибутом type и placeholder. Ниже ссылка, меняем в ней название на «Регистрация».


Затем, социальный блок и ссылку мы убираем, а в место них создаем отдельный блок с классом recover  под чекбокс и ссылкой для пользовательского соглашения.

В нем располагаем input с type checkbox, а ниже его label с ссылкой на соглашение.

Оформляем блоки с формами в css

Добавим для body задний фон, для этого я подготовил изображение, копирую его в основную директорию и подключаю на странице в теге body.

Оформляем вкладки

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


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

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

Для этого сделаем фон формы прозрачным на 20%, и этот фон будет как бы внешней его частью, а самой форме зададим белый цвет, для этого классу dws-form добавляем соответствующий background, а белого цвета.

.dws-form {
 background: rgba(255, 255, 255, 0.2);
…

.tab-form {
 background: #fff;
}


Затем, нам нужно показать, как будет выгладить активная и неактивная вкладка.

Наипростейший вариант, это для вкладок label задать отдельный класс tab, который по другому можно оформить.

Добавляем его и в CSS сразу опишем его стили.

Предварительная подготовка файлов

Делаем общею разметку, а далее оформляем блоки при помощи CSS.


Сделаем index файл и прописываем в нем DOCTYPE. 

Вставляем заголовок «Форма для авторизации», мета тег viewport оставим можно не прописывать но лишним не будет. Подключаем jquery, при помощи его покажу как реализовать ряд эффектов как на нем, так и сравним его с CSS. Ниже подключаем иконки через bootstrapcdn, и далее файл стилей, который позже создадим.

В самой структуре расположен блок с классом dws-wrapper, в нем буду описывать html разметку , а затем при помощи данного класса все выровним посередине экрана. Я его использую для удобства записи и просмотра текущего видео урока. Так что сам класс этот не обязательный, и в своих примерах можете его не прописывать.

Проверяем работоспособность vue


Вроде бы, чего там может сломаться? Нам же практически не пришлось лезть в код vue, за исключением добавления кнопки Выйти в шапке админки.

Но попробуем пересобрать админку, чтобы убедиться, что все хорошо. Сначала в production режиме

    npm run build

Вместо заключения

Проблемы с dev режимом во vue – это неприятный момент. Мы хотим и апишечку подергать, и все удобства vue-cli использовать. И на елку влезть, и ничего не ободрать.
Возможно, есть более изящный способ обойти эти проблемы в dev режиме, но я их пока не нашел. Поэтому приходится подпирать код лишними условиями.

Похожее:  Как я не получил доступ к личному кабинету. Начало. | RegaFAQ

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

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