TL;DR: Spring Security + JWT: за 10 минут добавляем безопасную аутентификацию и авторизацию в проект на Spring Boot.

Коротко: В статье показано, как быстро добавить JWT‑аутентификацию в проект на Spring Boot с использованием Spring Security. Вы увидите пошаговый пример конфигурации, генерации токенов и защиты эндпоинтов за 10 минут. Это идеальный старт для тех, кто хочет быстро интегрировать безопасную авторизацию в своё приложение.

Введение: Что такое Spring Security + JWT и зачем использовать их в микросервисах

Когда я впервые задумался о создании микросервисной архитектуры, сразу понял, что безопасность – это не просто «красивый штрих», а фундамент, который должен держать всё целиком. В этом контексте Spring Security и Spring Boot стали для меня как два мощных инструмента, готовых к работе: Spring Security обеспечивает гибкую и расширяемую систему аутентификации и авторизации, а Spring Boot позволяет быстро стартовать и сосредоточиться на бизнес‑логике, а не на настройке окружения.

Но как же сделать так, чтобы каждый сервис в кластере «знал» о пользователе, не пересылая пароли по сети и не запрашивая базу каждый раз? Здесь вступает в игру JWT – JSON Web Token. Внутри Spring Security JWT можно использовать как «пакет» сессии: после успешной аутентификации в одном сервисе клиент получает токен, который содержит все необходимые claims (имя, роли, срок действия). Далее этот токен просто ставится в заголовок Authorization и передаётся любому другому микросервису. Spring Security, настроенный на проверку подписи и сроков жизни токена, мгновенно распознаёт пользователя и применяет нужные правила доступа. Это избавляет от постоянных запросов к центральному хранилищу и делает микросервисы действительно независимыми, но при этом защищёнными.

В статье «spring‑boot‑что‑это» уже упоминалось, как быстро можно поднять базовую конфигурацию Spring Boot. Добавить к ней JWT‑проверку – всего несколько строк кода, и вы получаете надёжную, масштабируемую систему, где безопасность не мешает, а ускоряет разработку.

Подготовка проекта: Создание Spring Boot приложения с помощью стартеров и автоконфигурации

Я решил начать с чистого листа и воспользоваться официальным генератором проектов – Spring Initializr. Открыв https://start.spring.io/, я выбрал Java 21, Maven как систему сборки, и указал группу com.example и артефакт jwt-demo. В разделе «Dependencies» добавил два ключевых стартеров: Spring Web и Spring Security. Благодаря автоинжекции и автоконфигурации эти модули сразу попали в pom.xml, где появилось <dependency>org.springframework.boot:spring-boot-starter-web</dependency> и <dependency>org.springframework.boot:spring-boot-starter-security</dependency>. Это избавляет от ручного прописывания spring-boot-starter и всех его транзитивных зависимостей, таких как spring-webmvc, spring-security-web, spring-security-config и др.

После скачивания архива я распаковал его и открыл в IntelliJ IDEA. Если бы я предпочёл Gradle, то просто выбрал его в Initializr и заменил pom.xml на build.gradle.kts, где аналогично подключаются implementation("org.springframework.boot:spring-boot-starter-web") и implementation("org.springframework.boot:spring-boot-starter-security"). В обоих случаях автоконфигурация Spring Boot автоматически создаёт DispatcherServlet для веб‑части и базовую конфигурацию безопасности, которую мы будем тонко настроить позже. Это уже даёт нам полностью готовую основу, над которой можно быстро приступить к реализации JWT‑аутентификации.

Конфигурация Maven/Gradle: Добавление зависимостей JWT и Spring Security

Когда я впервые разрабатывал REST‑API, сразу понял, что без JWT сложно организовать надёжную аутентификацию. В этом разделе покажу, как быстро добавить нужные библиотеки в ваш проект, будь то Maven или Gradle.

В pom.xml для Maven просто вставьте зависимость от Spring Security и одну от jjwt, которая умеет генерировать и парсить токены.

<dependencies>
    <!-- Spring Security -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
    </dependency>
    <!-- JWT -->
    <dependency>
        <groupId>io.jsonwebtoken</groupId>
        <artifactId>jjwt-api</artifactId>
        <version>0.11.5</version>
    </dependency>
    <dependency>
        <groupId>io.jsonwebtoken</groupId>
        <artifactId>jjwt-impl</artifactId>
        <version>0.11.5</version>
        <scope>runtime</scope>
    </dependency>
    <dependency>
        <groupId>io.jsonwebtoken</groupId>
        <artifactId>jjwt-jackson</artifactId> <!-- для JSON‑парсинга -->
        <version>0.11.5</version>
        <scope>runtime</scope>
    </dependency>
</dependencies>

Если же вы пользуетесь Gradle, то в build.gradle.kts добавьте:

dependencies {
    implementation("org.springframework.boot:spring-boot-starter-security")
    implementation("io.jsonwebtoken:jjwt-api:0.11.5")
    runtimeOnly("io.jsonwebtoken:jjwt-impl:0.11.5")
    runtimeOnly("io.jsonwebtoken:jjwt-jackson:0.11.5") // Jackson‑плагин
}

После этого ваш проект будет готов к работе с JWT, а в следующих разделах мы разберём, как настроить сам JwtTokenProvider и интегр

Создание модели пользователя: Java Spring entity и репозиторий

Когда я начал писать модель пользователя, сразу подумал о том, как сделать её максимально «Spring‑friendly». В итоге я создал класс User, помеченный аннотацией @Entity, чтобы Hibernate мог его распознать и превратить в таблицу. Внутри я добавил поля id, username, password и role. Важно было объявить id как @GeneratedValue, чтобы база сама генерировала уникальный ключ. В качестве примера:

@Entity
@Table(name = "users")
public class User {
    @Id @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(unique = true, nullable = false)
    private String username;

    @Column(nullable = false)
    private String password;

    @Column(nullable = false)
    private String role;
}

Затем я создал репозиторий, наследующий от JpaRepository<User, Long>. Это дало мне сразу доступ к методам save, findById, findAll и т.д., без лишней ручной реализации. Я добавил кастомный метод findByUsername, чтобы быстро находить пользователя по логину:

public interface UserRepository extends JpaRepository<User, Long> {
    Optional<User> findByUsername(String username);
}

## Генерация JWT: Реализация сервиса генерации и проверки токенов

Когда я приступил к созданию JwtTokenProvider, сразу понял, что это будет ядро всей аутентификации. Внутри Spring Boot я создал отдельный сервис, помеченный аннотацией @Service, который будет отвечать за генерацию и валидацию токенов. В конструкторе я внедрил пару ключевых компонентов: SecretKey для подписи, Duration для срока жизни токена и JwtEncoder из Spring Security. Всё это позволяет держать логику в одном месте и легко менять параметры без пересборки приложения.  

При аутентификации я беру объект Authentication, извлекаю из него имя пользователя и роли, а затем формирую Claims с помощью JwtClaimsSet.builder().setSubject(userDetails.getUsername()).claim("roles", userDetails.getAuthorities()).setIssuedAt(Instant.now()).setExpiration(Instant.now().plus(tokenValidity)).build(). После этого токен подписывается и возвращается клиенту в виде строки. Если в запросе уже присутствует JWT, то JwtTokenProvider просто проверяет подпись, срок действия и наличие нужных ролей, чтобы решить, можно ли продлить сессию. Всё это реализовано на чистом Java Spring, используя возможности Spring Security и Spring Boot, что делает код лаконичным и легко расширяемым.

## Конфигурация Spring Security: Настройка WebSecurityConfigurerAdapter и фильтров

Я сразу погрузился в настройку `SecurityFilterChain`, ведь именно он управляет тем, какие запросы проходят через цепочку фильтров, а какие  нет. В Spring Boot 3.2 автоконфигурация упрощает жизнь: достаточно объявить бина `SecurityFilterChain` в конфигурации и указать, что все запросы к `/api/**` должны проходить через JWTаутентификацию. Я написал метод `securityFilterChain(HttpSecurity http)`, где включил CORS, отключил CSRF (мы работаем с токенами), а затем добавил свой `JwtAuthenticationFilter` через `http.addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class)`. Это гарантирует, что токен будет проверяться до того, как Spring попытается выполнить стандартную аутентификацию.

Для самого фильтра я использовал `OncePerRequestFilter`. Внутри `doFilterInternal` я вытаскиваю заголовок `Authorization`, проверяю, начинается ли он с `Bearer `, и если да, то декодирую токен, создаю `UsernamePasswordAuthenticationToken` и ставлю его в `SecurityContextHolder`. Если токен недействителен  просто пропускаю запрос дальше, чтобы Spring смог вернуть 401. Такой подход полностью соответствует рекомендациям Spring Framework и позволяет быстро перейти от простого RESTAPI к защищенному сервису без лишних усилий.

## Аутентификация через REST: Создание эндпоинта /login и возврат JWT

Наконец дошло время написать самую вкусную часть: AuthController, который выдаёт JWT после проверки логина и пароля. Я создаю класс с аннотацией `@RestController` и маппингом `/login`. Внутри я внедряю `AuthenticationManager` из Spring Security и наш собственный `JwtTokenProvider`. В методе `authenticate` принимаем DTO с полями `username` и `password`, а затем вызываем `authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(user, pass))`. Если проверка проходит, генерируем токен через `jwtTokenProvider.createToken(authentication.getName(), authentication.getAuthorities())` и возвращаем его клиенту в теле ответа.  

В микросервисной архитектуре этот эндпоинт становится точкой входа для всех сервисов, которые нуждаются в аутентификации. Я добавляю простую обработку ошибок: если `BadCredentialsException` выбрасывается, возвращаем статус 401 с понятным сообщением. Это делает сервис надёжным и понятным для фронтенда. В итоге, всего за пару строк кода у меня готов полностью рабочий RESTэндпоинт, выдающий JWT, который можно использовать в дальнейшем для защиты остальных микросервисов.

## Защита эндпоинтов: Применение аннотаций @PreAuthorize и ролей

Когда я подключал Spring Security к своему проекту на Spring Boot, сразу захотелось сделать так, чтобы только администраторы могли работать с разделом `/admin/**`. Самый простой и «чистый» способ  использовать аннотацию `@PreAuthorize` в контроллере. В файле конфигурации я включил поддержку аннотаций, добавив `@EnableGlobalMethodSecurity(prePostEnabled = true)`. Теперь в методах, которые обслуживают `/admin/**`, можно писать:

```java
@PreAuthorize("hasRole('ADMIN')")
@GetMapping("/admin/dashboard")
public String adminDashboard() {
    return "admin/dashboard";
}

Такой подход позволяет держать бизнес‑логику и безопасность рядом, а не в одном месте. Важно помнить, что роль в JWT должна быть записана как ROLE_ADMIN, иначе Spring не распознает её. Если в токене будет ADMIN, Spring добавит префикс ROLE_ автоматически. Благодаря @PreAuthorize любой запрос к /admin/** от пользователя без роли ADMIN будет автоматически отклонён с кодом 403. Это делает защиту надёжной и легко масштабируемой, ведь менять правила можно просто обновив аннотации, без изменения конфигурации безопасности.

Тестирование и отладка: Проверка работы JWT в Postman и unit‑тестах

После того как мы настроили фильтр JWT и интегрировали его в Spring Security, пришло время убедиться, что всё работает так, как задумано. Я открыл Postman, сконфигурировал запрос к /auth/login и получил токен. Далее я копирую его в заголовок Authorization: Bearer <token> и отправляю запрос к защищенному эндпоинту /api/secure. Если получаем 200 OK и ожидаемый JSON, значит фильтр корректно извлекает токен и передаёт его дальше. Если же сервер отвечает 401 Unauthorized, я сразу проверяю, не пропал ли пробел после Bearer и правильно ли выставлен Content-Type. В Postman можно быстро менять токен, проверять его истечение и убедиться, что при старом токене сервер действительно отклоняет запрос.

Для автоматизации я написал unit‑тесты с использованием @SpringBootTest и MockMvc. В JwtAuthenticationFilterTest я генерирую тестовый токен через JwtTokenProvider, затем отправляю запрос к защищенному контроллеру, передавая токен в заголовке. С помощью andExpect(status().isOk()) проверяю, что эндпоинт доступен. Также я добавил тест, где токен не передаётся – ожидаю 401. Всё это собирается и запускается через Maven (mvn test), а отчёт выводится в консоль. Такой подход позволяет быстро обнаруживать регрессии в логике аутентификации и гарантировать, что

Развертывание микросервиса: Docker‑образ и настройка переменных окружения

Наконец дошёл момент, когда всё, что мы писали, можно запаковать в контейнер и развернуть в реальной среде. Открываю новый файл Dockerfile и пишу:

FWCEEROOXNORPPTMKYORDSYoItEPpRaOer8In/g0Njae8Tdpt0kp/[:*"1.j7ja-avjrad"ka,-p"sp-l.jijamarr","app.jar"]

Коротко, но ёмко: берём легковесный образ OpenJDK, копируем наш JAR, открываем порт и указываем команду запуска. Благодаря автоконфигурации Spring Boot всё, что нужно, уже настроено – только переменные окружения.

Собираю образ командой docker build -t auth-service . и запускаю:

doc---keeperSJ8PW0rRT8uI_0nNS:GE8-_C0dPR8RE0OT-F=anIsuaLutmEpheSe-_rsaASeuCertTcvhIriVec\Ete=Kperyd\

Здесь -e передаёт секреты в контейнер, а SPRING_PROFILES_ACTIVE переключает профиль на продакшн. Таким образом микросервис получает все нужные данные без лишних файлов конфигурации, а Docker гарантирует изоляцию и воспроизводимость. Это именно то, что делает микросервисы гибкими и масштабируемыми.

Итог

FAQ

Какой алгоритм подписи JWT лучше использовать? Рекомендуется RS256 (асимметричный) для production — приватный ключ для подписи, публичный для проверки. HS256 (симметричный) проще, но требует общего секрета.

Как настроить refresh-токены в Spring Security? Создайте отдельный эндпоинт /auth/refresh, который принимает refresh-токен, проверяет его подпись и срок действия, и возвращает новый access-токен. Храните refresh-токены в безопасном cookie или в БД.

Как защитить разные эндпоинты разными ролями? Используйте @PreAuthorize с выражениями hasRole, hasAuthority или hasAnyRole. Включите @EnableGlobalMethodSecurity(prePostEnabled=true) и настройте роли в claims JWT.