OAuth 2.0 и JWT авторизация на Java: Полное руководство

Онлайн-тренажер Python для начинающих

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

Начать курс

Введение в OAuth 2.0 и JWT авторизацию на Java

Современные веб-приложения и API требуют надежных механизмов аутентификации и авторизации. Два стандарта, которые стали де-факто в мире Java-разработки — это OAuth 2.0 и JWT (JSON Web Tokens). OAuth 2.0 предоставляет протокол для делегирования доступа, а JWT — компактный и самодостаточный формат передачи данных. В этой статье мы разберем, как объединить эти технологии на Java с использованием Spring Security и популярных библиотек.

Вы узнаете, как настроить сервер авторизации, генерировать и проверять JWT-токены, а также защитить свои REST-эндпоинты. Материал будет полезен как начинающим, так и опытным Java-разработчикам, желающим углубить знания в области безопасности.

Основы OAuth 2.0: Потоки и роли

OAuth 2.0 определяет четыре роли: Resource Owner (владелец ресурса), Client (приложение), Authorization Server (сервер авторизации) и Resource Server (сервер ресурсов). Наиболее популярные потоки (grants) включают:

  • Authorization Code — для веб-приложений с серверной частью.
  • Client Credentials — для сервер-серверной коммуникации.
  • Resource Owner Password — для доверенных клиентов (устаревает).
  • Implicit — для SPA (больше не рекомендуется).

На Java мы можем реализовать OAuth 2.0 сервер с помощью spring-security-oauth2-authorization-server. Рассмотрим пример настройки базового сервера авторизации.

Настройка Authorization Server на Spring Boot

Добавьте зависимости в pom.xml:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-oauth2-authorization-server</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

Создайте конфигурацию SecurityConfig.java:

@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Bean
    @Order(1)
    public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http)
            throws Exception {
        OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http);
        return http.build();
    }

    @Bean
    @Order(2)
    public SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http)
            throws Exception {
        http.authorizeHttpRequests(authorize -> authorize
                .anyRequest().authenticated())
            .formLogin(Customizer.withDefaults());
        return http.build();
    }

    @Bean
    public RegisteredClientRepository registeredClientRepository() {
        RegisteredClient registeredClient = RegisteredClient.withId("client-id")
                .clientId("my-client")
                .clientSecret("{noop}secret")
                .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
                .redirectUri("http://localhost:8080/login/oauth2/code/my-client")
                .scope("read")
                .scope("write")
                .build();
        return new InMemoryRegisteredClientRepository(registeredClient);
    }
}

JWT: Структура и генерация токенов

JWT состоит из трех частей: Header, Payload и Signature. Header содержит тип токена и алгоритм шифрования. Payload включает claims (утверждения), такие как sub (субъект), exp (время истечения) и roles. Signature обеспечивает целостность данных.

Для работы с JWT на Java используем библиотеку io.jsonwebtoken:jjwt. Добавьте зависимость:

<dependency>
    <groupId>io.jsonwebtoken>
    <artifactId>jjwt-api</artifactId>
    <version>0.12.5</version>
</dependency>
<dependency>
    <groupId>io.jsonwebtoken>
    <artifactId>jjwt-impl</artifactId>
    <version>0.12.5</version>
    <scope>runtime</scope>
</dependency>
<dependency>
    <groupId>io.jsonwebtoken>
    <artifactId>jjwt-jackson</artifactId>
    <version>0.12.5</version>
    <scope>runtime</scope>
</dependency>

Создание JWT Token Provider

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

import io.jsonwebtoken.*;
import io.jsonwebtoken.security.Keys;
import org.springframework.stereotype.Component;

import javax.crypto.SecretKey;
import java.util.Date;

@Component
public class JwtTokenProvider {

    private final SecretKey secretKey = Keys.hmacShaKeyFor("my-secret-key-must-be-at-least-256-bits-long".getBytes());
    private final long expirationMs = 3600000; // 1 час

    public String generateToken(String username, String role) {
        Date now = new Date();
        Date expiryDate = new Date(now.getTime() + expirationMs);

        return Jwts.builder()
                .subject(username)
                .claim("role", role)
                .issuedAt(now)
                .expiration(expiryDate)
                .signWith(secretKey)
                .compact();
    }

    public String getUsernameFromToken(String token) {
        return parseClaims(token).getSubject();
    }

    public boolean validateToken(String token) {
        try {
            parseClaims(token);
            return true;
        } catch (JwtException | IllegalArgumentException e) {
            return false;
        }
    }

    private Claims parseClaims(String token) {
        return Jwts.parser()
                .verifyWith(secretKey)
                .build()
                .parseSignedClaims(token)
                .getPayload();
    }
}

Интеграция OAuth 2.0 и JWT с Resource Server

После получения access token от Authorization Server, клиент отправляет его в заголовке Authorization: Bearer <token>. Resource Server должен проверить токен. Настроим Spring Security для работы с JWT.

Конфигурация Resource Server

@Configuration
@EnableWebSecurity
public class ResourceServerConfig {

    @Value("${spring.security.oauth2.resourceserver.jwt.issuer-uri}")
    private String issuerUri;

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http.authorizeHttpRequests(authorize -> authorize
                .requestMatchers("/api/public/**").permitAll()
                .requestMatchers("/api/admin/**").hasRole("ADMIN")
                .anyRequest().authenticated())
            .oauth2ResourceServer(oauth2 -> oauth2
                .jwt(jwt -> jwt
                    .jwtAuthenticationConverter(jwtAuthenticationConverter                

Блоги

Рекомендаций по книгам