← Назад к вопросам

Что такое Authenticator в Retrofit?

2.0 Middle🔥 192 комментариев
#Сетевое взаимодействие

Комментарии (2)

🐱
deepseek-v3.2PrepBro AI6 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Что такое Authenticator в Retrofit?

Authenticator в Retrofit — это механизм, который автоматически обрабатывает ошибки аутентификации (обычно HTTP-код 401 Unauthorized) и пытается обновить или получить новые учетные данные (например, токен доступа) без необходимости прерывания запроса на уровне вызывающего кода. Это ключевой компонент для реализации безопасного и устойчивого взаимодействия с API, требующими аутентификации, особенно при использовании токенов с ограниченным сроком действия (JWT, OAuth).

Основная цель и принцип работы

Когда Retrofit выполняет HTTP-запрос через OkHttpClient, и сервер возвращает ответ с кодом 401, цепочка вызовов Interceptor-ов может обработать эту ситуацию. Однако Interceptor работает на уровне каждого запроса и не предназначен для повторных попыток с обновленными данными. Здесь на сцену выходит Authenticator.

Его работа строится по следующему алгоритму:

  1. Обнаружение ошибки: OkHttp определяет, что ответ имеет код 401 (или другой, указанный в конфигурации).
  2. Вызов Authenticator: Создается объект Request (оригинальный запрос) и Response (ответ с ошибкой). Эти данные передаются в метод authenticate() интерфейса Authenticator.
  3. Логика обновления учетных данных: Внутри authenticate() разработчик реализует логику для получения нового токена (например, синхронный запрос к эндпоинту /refresh).
  4. Повтор запроса: Если новые учетные данные получены успешно, метод возвращает новый объект Request, который является копией оригинального, но с обновленными заголовками авторизации (например, Authorization: Bearer <new_token>).
  5. Автоматический повтор: OkHttp автоматически повторяет исходный запрос с этими новыми заголовками.

Практическая реализация

Рассмотрим пример реализации для работы с JWT-токеном, который требует периодического обновления через Refresh Token.

1. Создание класса Authenticator:

import okhttp3.Authenticator
import okhttp3.Request
import okhttp3.Response
import javax.inject.Inject
import javax.inject.Singleton

@Singleton
class TokenAuthenticator @Inject constructor(
    private val tokenManager: TokenManager
) : Authenticator {

    override fun authenticate(route: Route?, response: Response): Request? {
        // Избегаем бесконечного цикла: не пытаемся обновить токен, если это уже запрос на обновление.
        if (response.request.url.pathSegments.contains("refresh")) {
            return null // Приведет к завершению с исходной ошибкой 401.
        }

        // Синхронно обновляем токен. Этот код выполняется на фоновом потоке OkHttp.
        val newToken = tokenManager.refreshToken() // Блокирующий вызов!

        return if (newToken != null) {
            // Создаем новый запрос с обновленным заголовком авторизации.
            response.request.newBuilder()
                .header("Authorization", "Bearer $newToken")
                .build()
        } else {
            null // Не удалось обновить токен -> пробрасываем ошибку 401 клиенту.
        }
    }
}

2. Класс управления токенами (упрощенный):

import retrofit2.Retrofit
import javax.inject.Inject
import javax.inject.Singleton

@Singleton
class TokenManager @Inject constructor(
    private val authApi: AuthApi,
    private val securePrefs: SecurePreferences
) {

    // Синхронный метод обновления токена. Должен быть потокобезопасным!
    fun refreshToken(): String? {
        synchronized(this) {
            val refreshToken = securePrefs.getRefreshToken() ?: return null
            try {
                val response = authApi.refreshToken(refreshToken).execute() // Синхронный запрос!
                if (response.isSuccessful) {
                    val newAccessToken = response.body()?.accessToken
                    newAccessToken?.let { securePrefs.saveAccessToken(it) }
                    return newAccessToken
                }
            } catch (e: Exception) {
                // Обработка ошибок сети или сервера.
            }
            // Если обновление не удалось, здесь можно вызвать логаут.
            return null
        }
    }
}

3. Настройка OkHttpClient:

val okHttpClient = OkHttpClient.Builder()
    .authenticator(TokenAuthenticator(tokenManager)) // Устанавливаем наш Authenticator
    .addInterceptor(HttpLoggingInterceptor()) // Interceptor для логирования
    .build()

val retrofit = Retrofit.Builder()
    .baseUrl(BASE_URL)
    .client(okHttpClient)
    .addConverterFactory(GsonConverterFactory.create())
    .build()

Ключевые отличия от Interceptor

  • Interceptor перехватывает каждый запрос для добавления заголовков (например, текущего токена) и часто используется для логирования.
  • Authenticator активируется только при специфических ошибках ответа (как 401) и отвечает за получение новых учетных данных и повтор запроса.

Важные аспекты и лучшие практики

  1. Синхронность: Метод authenticate() должен быть синхронным и потокобезопасным. Все сетевые вызовы внутри него используют execute().
  2. Избегание циклических запросов: Критически важно добавить проверку, чтобы не пытаться обновить токен при запросе на сам эндпоинт обновления, иначе возникнет бесконечный цикл.
  3. Управление состоянием: Реализация должна учитывать параллельные запросы. Если несколько запросов одновременно получат 401, authenticate() может быть вызван несколько раз. Стандартное решение — использование синхронизации (synchronized) или мьютексов для гарантии, что запрос на обновление токена выполнится только один раз.
  4. Обработка неудачи: Если обновить токен не удалось, следует вернуть null. Это приведет к провалу исходного запроса с ошибкой 401, и приложение сможет перенаправить пользователя на экран логина.

Таким образом, Authenticator — это мощный и элегантный паттерн, который инкапсулирует сложную логику обработки просроченных сессий, делая код клиента чистым, устойчивым и соответствующим современным стандартам безопасности.

Что такое Authenticator в Retrofit? | PrepBro