Ссылка на проект: GitLab
Напишем простой сервис аутентификации с выдачей JWToken. Для реализации будем использовать Java 17, SpringBoot 3.2.0, h2, Maven в памяти.
Cоздадим и настроим проект https://start.spring.io/
![SpringInitializer SpringInitializer](https://habrastorage.org/getpro/habr/upload_files/665/e3f/470/665e3f470d3e4da5abb264383ae98877.png)
Нам понадобится:
Web
Security
JPA
H2 Database
Lombok
Настроим подключение к нашей БД которая будет находится в памяти, так же сервер будем запускать на порту 9090
![application.properties application.properties](https://habrastorage.org/getpro/habr/upload_files/96e/d0c/60f/96ed0c60f1a109abf6676b692e092516.png)
Для автоматического заполнения БД создадим пару файлов с созданием и заполнением таблиц data.sql и schema.sql.
Содержание файлов data && schema
![data.sql data.sql](https://habrastorage.org/getpro/habr/upload_files/929/91f/2a0/92991f2a0ce133983aaa219dde664f15.png)
![schema.sql schema.sql](https://habrastorage.org/getpro/habr/upload_files/e5d/c1a/725/e5dc1a7250d3efca1effac9c631ca276.png)
Далее нам потребуется создать сопутствующие сущности User, Role
![User User](https://habrastorage.org/getpro/habr/upload_files/3ea/f7f/d79/3eaf7fd792c302f9ab9850269c9b9de1.png)
![Role Role](https://habrastorage.org/getpro/habr/upload_files/873/c2f/7e8/873c2f7e8d534ec43879fc3ef809405b.png)
Закончили с базовой настройкой, переходим к основному классу WebConfiguration настройки Security. В нем мы должны настроить bean SecurityFilterChain, так же создадим bean PasswordEncoder для возможности шифрования пароля пользователя.
![WebConfiguration.jav WebConfiguration.jav](https://habrastorage.org/getpro/habr/upload_files/4df/3d6/262/4df3d62621623912d0c9fa4167d061c8.png)
Для шифрование пароля будем использовать 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](https://habrastorage.org/getpro/habr/upload_files/e78/4fe/764/e784fe76455a33761c5049ad16633453.png)
Данный класс будет расширять OncePerRequestFilter и переопределять два метода
doFilterInternal
shouldNotFilter
Данный фильтр проверяет header Authorization, если он пустой то проверяет передано ли в теле запроса JSON с именем и паролем для аутентификации, проверят пользователя и если все ок выдает JWToken.
Рассмотрим более подробно данный класс.
Для формирования JWToken и проверки пользователя создадим и заинжектим два класса
private final JwtService jwtService;
private final UsernamePasswordAuthenticationProvider authenticationProvider;
![JwtService.java JwtService.java](https://habrastorage.org/getpro/habr/upload_files/b43/ccf/cd6/b43ccfcd6b1d8259346db2880ba85ed2.png)
Для работы с JWT потребуются следующие библиотеки
![pom.xml pom.xml](https://habrastorage.org/getpro/habr/upload_files/3ba/3b2/186/3ba3b218692b53c301b53812d0a2050a.png)
Здесь мы будем генерировать ключ подписи и собственно сам JWT.
Keys.hmacShaKeyFor(signingKey.getBytes(StandardCharsets.UTF_8));
шифруем ключ BASE64
В методе generatedJwt строим JWT.
Задаем поля в payload и нагрузку, устанавливаем дату окончания действия и ключ подписи
payload {
"role"
"user_id
"username"
"exp"
"sub"
}
![UsernamePasswordAuthenticationProvider.java UsernamePasswordAuthenticationProvider.java](https://habrastorage.org/getpro/habr/upload_files/dad/ece/6aa/dadece6aa3aae56dae166bffec3f94b0.png)
Данный класс проверяет наличие пользователя и корректность пароля и возвращает аутентификацию.
Класс UserDetailsService возвращает пользователя если он имеется в базе
![UserService.java UserService.java](https://habrastorage.org/getpro/habr/upload_files/82d/83e/4a9/82d83e4a906e2c901857df809b918e72.png)
Вернемся к классу 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
![](https://habrastorage.org/getpro/habr/upload_files/cee/5ea/7e2/cee5ea7e272b618a050da7a4cf5b4c24.png)
Видим, что появился header
Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJyb2xlIjpbIlJPTEVfQURNSU4iLCJST0xFX1VTRVIiXSwidXNlcl9pZCI6IjEiLCJ1c2VybmFtZSI6ImFkbWluIiwiZXhwIjoxNzAzMTU1NzAxLCJzdWIiOiJhZG1pbiJ9.aNHtaBa-7WDXO_MMl83MG9wxTO0MnMmEwdjgzSOrh0g
![Содержание JWToken Содержание JWToken](https://habrastorage.org/getpro/habr/upload_files/c64/6a2/b4e/c646a2b4ed4e471e9e5d65e09f6353af.png)
Данный пример демонстрирует как достаточно быстро и просто поднять сервис аутентификации и выдачи JWT. Так же можно добавить проверку токена, выдача refresh token и запрос со стороннего сервиса, но это уже другая история.
Комментарии (6)
Rockway
17.12.2023 05:48Благодарю за статью, все просто и понятно, за ссылку на источник отдельное спасибо. Сегодня со статьи переписал весь код, интересно было потренироваться, понять некоторые моменты.
SilverRid Автор
17.12.2023 05:48Спасибо, по security, еще много чего интересного, буду выкладывать по возможности.
Shiko_Siberia
17.12.2023 05:48SilverRid Автор
17.12.2023 05:48Посмотрел видео, да действительно, видимо таков наш путь. Это моя первая статья, так сакзать проба пера, всегда готов дополнять, изменять. Если вас не затруднит напишите чего бы вам хотелось увидеть в этой или возможно новой статье.
Belvarm
Приложите пожалуйста ссылку на гит, а то с скринами не очень то удобно
SilverRid Автор
Ссылка в самом начале статьи, попробую выделить получше https://gitlab.com/SilverRid/SpringSecurityJWT