JavaRush /Курси /Модуль 5. Spring /Лекція 106: Використання Refresh токенів і їх налаштуванн...

Лекція 106: Використання Refresh токенів і їх налаштування

Модуль 5. Spring
Рівень 18 , Лекція 5
Відкрита

Access Token (токен доступу) — це ключ, який дозволяє користувачу взаємодіяти з захищеними ресурсами. Однак, з міркувань безпеки, його час життя обмежений. Наприклад, Access Token може бути валідним лише протягом 15 хвилин. Після закінчення терміну дії токена всі захищені запити почнуть повертати помилку 401 Unauthorized. Користувач у цьому випадку змушений заново проходити авторизацію, що точно викличе в нього невдоволення (і навряд чи він поставить вашому додатку 5 зірок в App Store).

Рішення: Refresh токени

Refresh токен призначений для оновлення простроченого Access Token, не вимагаючи від користувача повторного введення облікових даних. Цей крок робить взаємодію користувача з додатком плавною, а систему — зручнішою.

Як це працює?

  1. Після успішної авторизації сервер видає два токени:
    • Access Token — для доступу до захищених ресурсів.
    • Refresh Token — для отримання нового Access Token, коли старий прострочений.
  2. Коли строк дії Access Token закінчується, клієнт (наприклад, ваш мобільний або веб-додаток) відправляє Refresh Token на сервер.
  3. Сервер перевіряє Refresh Token і, якщо він дійсний, видає новий Access Token.
  4. Користувач продовжує роботу без повторної авторизації.

Налаштування і використання Refresh токенів в Spring

Давайте створимо додаток на Spring Boot, який буде використовувати Refresh токени для оновлення Access Token. Для цього потрібен вже існуючий додаток з JWT-аутентифікацією. Якщо ви робили вправи з попередніх лекцій, у вас вже є базова налаштування JWT. Якщо ні, рекомендую трохи повернутися назад і зробити це.

1. Генерація Refresh токенів

Почнемо зі зміни логіки видачі токенів. Тепер наш endpoint для аутентифікації буде повертати два токени.

Приклад коду генерації токенів:


@RestController
@RequestMapping("/auth")
public class AuthController {

    private final JwtService jwtService; // Сервіс для роботи з JWT
    private final UserService userService;

    @PostMapping("/login")
    public ResponseEntity
    login(@RequestBody LoginRequest request) {
        // Перевіряємо облікові дані користувача
        User user = userService.authenticate(request.getUsername(), request.getPassword());

        // Генеруємо Access і Refresh токени
        String accessToken = jwtService.generateAccessToken(user);
        String refreshToken = jwtService.generateRefreshToken(user);

        // Повертаємо їх у відповіді
        return ResponseEntity.ok(Map.of(
            "accessToken", accessToken,
            "refreshToken", refreshToken
        ));
    }
}

Зверніть увагу, що тепер ми використовуємо два різні методи для генерації Access і Refresh токенів. Access Token зазвичай має короткий строк дії (наприклад, 15 хвилин), тоді як Refresh Token — більш тривалий (наприклад, 7 днів).

2. Додавання endpoint для оновлення токенів

Тепер додамо endpoint, який приймає Refresh Token і повертає новий Access Token.

Приклад реалізації:


@RestController
@RequestMapping("/auth")
public class AuthController {

    private final JwtService jwtService;

    @PostMapping("/refresh")
    public ResponseEntity<?> refresh(@RequestBody Map<String, String> body) {
        String refreshToken = body.get("refreshToken");

        // Перевіряємо Refresh токен
        if (!jwtService.isValidRefreshToken(refreshToken)) {
            throw new ResponseStatusException(HttpStatus.UNAUTHORIZED, "Invalid Refresh Token");
        }

        // Генеруємо новий Access Token
        String newAccessToken = jwtService.generateAccessTokenFromRefreshToken(refreshToken);

        return ResponseEntity.ok(Map.of(
            "accessToken", newAccessToken
        ));
    }
}

3. Логіка перевірки і генерації токенів в JwtService

Додамо два нових методи в JwtService. Один для перевірки Refresh токена, а інший для генерації нового Access Token на його основі.

Приклад:


@Service
public class JwtService {

    private final String secretKey = "your-secret-key";  // Секретний ключ для підпису токенів

    public boolean isValidRefreshToken(String refreshToken) {
        try {
            Claims claims = Jwts.parser()
                .setSigningKey(secretKey)
                .parseClaimsJws(refreshToken)
                .getBody();

            // Переконайтесь, що це дійсно Refresh токен
            String tokenType = claims.get("type", String.class);
            return "refresh".equals(tokenType);
        } catch (JwtException e) {
            return false;
        }
    }

    public String generateAccessTokenFromRefreshToken(String refreshToken) {
        Claims claims = Jwts.parser()
            .setSigningKey(secretKey)
            .parseClaimsJws(refreshToken)
            .getBody();

        String username = claims.getSubject();

        // Створюємо новий Access Token
        return Jwts.builder()
            .setSubject(username)
            .setIssuedAt(new Date())
            .setExpiration(new Date(System.currentTimeMillis() + 15 * 60 * 1000)) // 15 хвилин
            .signWith(SignatureAlgorithm.HS512, secretKey)
            .compact();
    }
}

Тут важливо переконатися, що Refresh токен дійсно є Refresh токеном (наприклад, по додатковому полю типу в Payload).

4. Безпечна робота з Refresh токенами

Refresh токени більш вразливі, оскільки мають тривалий строк дії. Ось кілька рекомендацій для підвищення безпеки:

  • Зберігання на клієнті: ніколи не зберігайте Refresh токени в localStorage. Використовуйте HTTP-only cookies.
  • Протокол HTTPS: завжди використовуйте захищене з'єднання при передачі токенів.
  • Ротація токенів: після використання Refresh токена для оновлення Access Token, можете згенерувати новий Refresh Token і повернути його клієнту. Це зменшує ймовірність компрометації.
  • Список відкликаних токенів: у випадку виявлення витоку токена, ви маєте мати можливість відкликати Refresh токен. Зазвичай це роблять за допомогою бази даних для відстеження дійсних токенів.

5. Обробка помилок

Типові проблеми з Refresh токенами:

  • Прострочений токен: поверніть 401 Unauthorized і попросіть користувача пройти авторизацію знову.
  • Невалідний токен: якщо токен пошкоджений, відмовтеся від його обробки.
  • Підробка токена: використовуйте цифровий підпис (HMAC або RSA) для перевірки автентичності.

Приклад обробки помилок:


if (!jwtService.isValidRefreshToken(refreshToken)) {
    throw new ResponseStatusException(HttpStatus.UNAUTHORIZED, "Invalid Refresh Token");
}

Практичне застосування

Сьогодні вже важко уявити сучасний додаток без Refresh токенів. Наприклад:

  • У мобільних додатках, де важливо тримати користувача залогіненим тривалий час.
  • У веб-додатках, які повинні підтримувати фонові запити (наприклад, отримання нової стрічки новин).
  • Роботодавці цінують знання Refresh токенів, бо це демонструє розуміння принципів безпеки, користувацького досвіду і роботи з токенами.

Офіційна документація Spring Security по роботі з JWT і OAuth2 доступна тут.

Тепер ви готові до використання Refresh токенів у своїх додатках і можете суттєво покращити користувацький досвід і безпеку!

Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ