Простой сервис аутентификации. SpringBootSecurity

Моя цель - предложение широкого ассортимента товаров и услуг на постоянно высоком качестве обслуживания по самым выгодным ценам.

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

GitLab

Напишем простой сервис аутентификации с выдачей JWToken. Для реализации будем использовать Java 17, SpringBoot 3.2.0, h2, Maven в памяти.

Cоздадим и настроим проект https://start.spring.io/

SpringInitializer
SpringInitializer

Нам понадобится:

  • Web

  • Security

  • JPA

  • H2 Database

  • Lombok

Настроим подключение к нашей БД которая будет находится в памяти, так же сервер будем запускать на порту 9090

application.properties
application.properties

Для автоматического заполнения БД создадим пару файлов с созданием и заполнением таблиц data.sql и schema.sql.

Содержание файлов data && schema
data.sql
data.sql
schema.sql
schema.sql

Далее нам потребуется создать сопутствующие сущности User, Role

User
User
Role
Role

Закончили с базовой настройкой, переходим к основному классу WebConfiguration настройки Security. В нем мы должны настроить bean SecurityFilterChain, так же создадим bean PasswordEncoder для возможности шифрования пароля пользователя.

WebConfiguration.jav
WebConfiguration.jav

Для шифрование пароля будем использовать BCrypt.

Далее рассмотрим SecurityFilterChain:

http.headers(headers -> headers.frameOptions(HeadersConfigurer.FrameOptionsConfig::sameOrigin))
.csrf(AbstractHttpConfigurer::disable)
.cors(AbstractHttpConfigurer::disable);

В этой строчке мы отключаем CSRF && CORS так как для простого фунционала нам они не понадобятся.

В проде обязательно использовать CSRF && CORS

Т.к h2-console использует frame то для корректной работы нужно добавить header x-frame-options "SAMEORIGIN" или отключить его FrameOptionsConfig::disable

X-Frame-Options используется для предотвращения кликджекинга на сайте. Он определяет, разрешено ли браузеру отображать страницу в <frame>, <iframe>, <embed> или <object>
https://www.geeksforgeeks.org/http-headers-x-frame-options/

headers.frameOptions(HeadersConfigurer.FrameOptionsConfig::disable)
OR
headers.frameOptions(HeadersConfigurer.FrameOptionsConfig::sameOrigin)

Далее разрешим доступ к консоли h2 для всех пользователей и для остальных запросов требуется аутентификация

http.authorizeHttpRequests(authz ->
authz.requestMatchers("/h2-console/**").permitAll().anyRequest().authenticated());

Главное действие будет происходить в фильтрации запроса

http.addFilterAt(initialAuthenticationFilter, BasicAuthenticationFilter.class);

Здесь мы добавляем свой фильтр в цепочку фильтрации initialAuthenticationFilter который мы inject'им в методе
public SecurityFilterChain securityFilterChain(HttpSecurity http, InitialAuthenticationFilter initialAuthenticationFilter)

InitialAuthenticationFilter.java
InitialAuthenticationFilter.java

Данный класс будет расширять OncePerRequestFilter и переопределять два метода

  • doFilterInternal

  • shouldNotFilter

Данный фильтр проверяет header Authorization, если он пустой то проверяет передано ли в теле запроса JSON с именем и паролем для аутентификации, проверят пользователя и если все ок выдает JWToken.

Рассмотрим более подробно данный класс.

Для формирования JWToken и проверки пользователя создадим и заинжектим два класса

private final JwtService jwtService;
private final UsernamePasswordAuthenticationProvider authenticationProvider;

JwtService.java
JwtService.java

Для работы с JWT потребуются следующие библиотеки

pom.xml
pom.xml

Здесь мы будем генерировать ключ подписи и собственно сам JWT.

Keys.hmacShaKeyFor(signingKey.getBytes(StandardCharsets.UTF_8));

шифруем ключ BASE64

В методе generatedJwt строим JWT.

Задаем поля в payload и нагрузку, устанавливаем дату окончания действия и ключ подписи

payload {
"role"
"user_id
"username"
"exp"
"sub"
}

UsernamePasswordAuthenticationProvider.java
UsernamePasswordAuthenticationProvider.java

Данный класс проверяет наличие пользователя и корректность пароля и возвращает аутентификацию.

Класс UserDetailsService возвращает пользователя если он имеется в базе

UserService.java
UserService.java

Вернемся к классу InitialAuthenticationFilter.

Метод doFilterInternal

  • из request мы извлекаем JSON с логином и паролем

  • проеряем пользователя
    Authentication authentication = new UsernamePasswordAuthentication(username, password); authentication = authenticationProvider.authenticate(authentication);

  • если все ок выдаем JWT в response в заголовок Authorization: Bearer *****
    String jwt = jwtService.generatedJwt(authentication); response.setHeader("Authorization", HeaderValues.BEARER + jwt);

Метод shouldNotFilter

  • Позволяет применить фильтр к отпределенному/ым uri
    В данном случае применится к запросам на uri /login

Проверка

curl -v -d '{"username":"admin", "password":"123"}' -H "Content-Type: application/json" -X POST http://localhost:9090/login

Видим, что появился header

Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJyb2xlIjpbIlJPTEVfQURNSU4iLCJST0xFX1VTRVIiXSwidXNlcl9pZCI6IjEiLCJ1c2VybmFtZSI6ImFkbWluIiwiZXhwIjoxNzAzMTU1NzAxLCJzdWIiOiJhZG1pbiJ9.aNHtaBa-7WDXO_MMl83MG9wxTO0MnMmEwdjgzSOrh0g

Содержание JWToken
Содержание JWToken

Данный пример демонстрирует как достаточно быстро и просто поднять сервис аутентификации и выдачи JWT. Так же можно добавить проверку токена, выдача refresh token и запрос со стороннего сервиса, но это уже другая история.

Источник: https://habr.com/ru/articles/781066/


Интересные статьи

Интересные статьи

Прямого доступа к сервисам OpenAI в РФ, как известно, нет. Для работы с ChatGPT многие пользуются телеграм-ботами, чаще всего бесплатными. В основном, запросы к ним — непритязательные. Пользовател...
В Госдуме предложили запретить регистрацию на российских сайтах с помощью иностранной электронной почты. Госдума уже одобрила поправки во втором и третьем чтениях и сейчас проект рассматривается Совет...
Хочу рассказать вам о своем новом мини-проекте, который помогает ориентироваться в часовых поясах. Не буду томить, вот он:https://timezoned.vercel.appА теперь я расскажу, что это и зачем.
Добрая четверть моего рабочего времени за последний год ушла на обновление архитектуры Учи.ру. С ростом продуктов и количества пользователей увеличился и клубок зависимос...
Когда в Sports.ru понадобился свой WYSIWYG-редактор, мы решили сделать его на основе библиотеки ProseMirror. Одной из ключевых особенностей этого инструмента является модульность и широкие возмож...