Ответ
Retrofit сам по себе не обрабатывает просроченные токены, но можно использовать Interceptor для автоматического обновления токена при 401 ошибке.
Пример:
class AuthInterceptor(private val tokenManager: TokenManager) : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val request = chain.request().newBuilder()
.addHeader("Authorization", "Bearer ${tokenManager.getToken()}")
.build()
val response = chain.proceed(request)
if (response.code == 401) {
val newToken = tokenManager.refreshToken() // синхронный запрос
val newRequest = request.newBuilder()
.header("Authorization", "Bearer $newToken")
.build()
return chain.proceed(newRequest)
}
return response
}
}
Важно:
- Обновление токена должно быть синхронным внутри
intercept(). - Учитывайте возможные рекурсии (если refresh-токен тоже протух).
- Лучше добавить механизм блокировки параллельных обновлений токена.
Ответ 18+ 🔞
А, Retrofit, ёпта! Ну классика же. Сам он, конечно, как мартышлюшка с гранатой — в руки дали, а он нихуя не понимает, когда токен сдох. Надо ему всё на блюдечке с голубой каёмочкой принести.
Смотри, вот тебе простой рецепт, как эту манду с ушами заставить работать. Берёшь Interceptor, и там уже делаешь свою магию.
Вот смотри, пример кода, тут всё правильно:
class AuthInterceptor(private val tokenManager: TokenManager) : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val request = chain.request().newBuilder()
.addHeader("Authorization", "Bearer ${tokenManager.getToken()}")
.build()
val response = chain.proceed(request)
if (response.code == 401) {
val newToken = tokenManager.refreshToken() // синхронный запрос
val newRequest = request.newBuilder()
.header("Authorization", "Bearer $newToken")
.build()
return chain.proceed(newRequest)
}
return response
}
}
Выглядит вроде логично, да? Но тут, чувак, подозрение ебать чувствую, что сейчас накосячим. Запомни три вещи, а то будет тебе хиросима и нигасраки, а не авторизация.
Во-первых, этот refreshToken() — он должен быть синхронным, внутри intercept(). Нельзя тут корутины или колбэки городить, потому что метод intercept должен вернуть Response, а не твои хотелки. Если пойдёшь асинхронным путём — получишь хуй в пальто вместо ответа.
Во-вторых, представь ситуацию: пришёл запрос, токен протух. Ты пошёл обновлять, а refresh-токен-то тоже, сука, просрочен! Или сервер тебе говорит: «Иди нахуй, недействительный». Получается рекурсия — 401, обновляю, снова 401, снова обновляю... До бесконечности. Надо чётко это обрабатывать, а то уйдёшь в бесконечный цикл и накроешься медным тазом.
И в-третьих, самое жирное. Допустим, у тебя пять запросов полетели одновременно, и у всех токен сдох. Что делает твой код? Каждый поток, получив 401, побежит обновлять токен. Получится овердохуища одинаковых запросов на /refresh, сервер обоссытся, а ты получишь кучу ошибок. Надо ставить замок (synchronized или Mutex), чтобы пока один поток токен обновляет, остальные ждали, а не лезли как гомосеки налетели.
Короче, идея простая, но деталей — ядрёна вошь. Без них получится не решение, а пиздопроебибна.