OAuth 2.0 и JWT авторизация на Java: пошаговое руководство для начинающих

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

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

Начать курс

Введение в OAuth 2.0 и JWT на Java

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

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

Основы OAuth 2.0: как это работает

OAuth 2.0 определяет несколько ролей участников взаимодействия:

  • Resource Owner — пользователь, владеющий защищенными данными
  • Client — приложение, которое запрашивает доступ
  • Authorization Server — сервер, выдающий токены после аутентификации
  • Resource Server — сервер, хранящий защищенные ресурсы

Основная идея протокола — клиент получает временный токен доступа, который предъявляет ресурсному серверу. Самый распространенный сценарий — Authorization Code Flow, который используется в веб-приложениях.

Пример настройки OAuth 2.0 клиента в Spring Boot

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

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-oauth2-client</artifactId>
</dependency>

Настройте application.yml для работы с Google OAuth 2.0:

spring:
  security:
    oauth2:
      client:
        registration:
          google:
            client-id: YOUR_CLIENT_ID
            client-secret: YOUR_CLIENT_SECRET
            scope:
              - email
              - profile

Теперь Spring Security автоматически обработает редирект на страницу входа Google и вернет токен доступа.

JWT: структура и создание токена

JWT состоит из трех частей, разделенных точками:

  • Header — содержит тип токена и алгоритм подписи (например, HS256)
  • Payload — содержит утверждения (claims): данные пользователя, срок действия, роль
  • Signature — подпись, созданная из заголовка и полезной нагрузки с использованием секретного ключа

Для работы с JWT в Java чаще всего используют библиотеку jjwt. Добавьте зависимость:

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

Создание JWT токена на Java

Вот пример класса для генерации и валидации JWT:

import io.jsonwebtoken.*;
import io.jsonwebtoken.security.Keys;
import javax.crypto.SecretKey;
import java.util.Date;

public class JwtUtil {

    private final SecretKey secretKey = Keys.hmacShaKeyFor(
        "my-secret-key-which-should-be-at-least-256-bits-long".getBytes()
    );

    public String generateToken(String username, String role) {
        return Jwts.builder()
            .subject(username)
            .claim("role", role)
            .issuedAt(new Date())
            .expiration(new Date(System.currentTimeMillis() + 3600000)) // 1 час
            .signWith(secretKey)
            .compact();
    }

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

Обратите внимание: в реальном проекте секретный ключ должен храниться в безопасном месте (например, в переменных окружения или Vault).

Интеграция OAuth 2.0 и JWT в REST API

Типичная архитектура выглядит так:

  1. Пользователь входит через OAuth 2.0 провайдера (Google, GitHub)
  2. Сервер получает от провайдера токен доступа и информацию о пользователе
  3. Сервер создает собственный JWT токен с необходимыми утверждениями
  4. Клиент использует JWT для доступа к защищенным эндпоинтам

Фильтр JWT для Spring Security

Создайте фильтр, который перехватывает запросы и проверяет JWT:

import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.filter.OncePerRequestFilter;
import java.io.IOException;
import java.util.Collections;

public class JwtAuthenticationFilter extends OncePerRequestFilter {

    private final JwtUtil jwtUtil;

    public JwtAuthenticationFilter(JwtUtil jwtUtil) {
        this.jwtUtil = jwtUtil;
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request,
                                    HttpServletResponse response,
                                    FilterChain filterChain)
            throws ServletException, IOException {

        String authHeader = request.getHeader("Authorization");

        if (authHeader != null && authHeader.startsWith("Bearer ")) {
            String token = authHeader.substring(7);
            try {
                var claims = jwtUtil.validateToken(token);
                String username = claims.getSubject();
                String role = claims.get("role", String.class);

                var auth = new UsernamePasswordAuthenticationToken(
                    username, null, 
                    Collections.singleton(() -> "ROLE_" + role)
                );
                SecurityContextHolder.getContext().setAuthentication(auth);
            } catch (Exception e) {
                SecurityContextHolder.clearContext();
            }
        }
        filterChain.doFilter(request, response);
    }
}

Регистрация фильтра в конфигурации безопасности

Блоги

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