This commit is contained in:
Eric85710 2026-06-30 16:05:50 +08:00
parent 89da510a3f
commit f71cb04704

View file

@ -2,6 +2,7 @@ package com.example.login_v3.data.di
import androidx.lifecycle.ViewModel
import com.example.login_v3.data.api.TecnologiaApi
import com.example.login_v3.data.repository.basic.TokenAuthenticator
import com.example.login_v3.data.repository.basic.TokenManager
import com.squareup.moshi.Moshi
import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory
@ -12,7 +13,9 @@ import dagger.hilt.android.lifecycle.HiltViewModel
import dagger.hilt.components.SingletonComponent
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.firstOrNull
import kotlinx.coroutines.runBlocking
import okhttp3.Interceptor
import okhttp3.OkHttpClient
import retrofit2.Retrofit
import retrofit2.converter.moshi.MoshiConverterFactory
@ -32,39 +35,49 @@ object NetworkModule {
.build()
}
// TokenInterceptor
@Provides
@Singleton
fun provideOkHttpClient(tokenManager: TokenManager): OkHttpClient {
fun provideTokenInterceptor(tokenManager: TokenManager): Interceptor {
return Interceptor { chain ->
val originalRequest = chain.request()
val path = originalRequest.url.encodedPath
// 如果是認證相關的 API,直接放行,不附加 Token Header
if (path.contains("api/auth/login") ||
path.contains("api/auth/register") ||
path.contains("api/auth/refresh") ||
path.contains("api/health")) {
return@Interceptor chain.proceed(originalRequest)
}
// 在背景執行緒中安全地獲取當前 Token
val token = runBlocking(Dispatchers.IO) {
tokenManager.currentAccessToken.firstOrNull()
}
val requestBuilder = originalRequest.newBuilder()
if (!token.isNullOrEmpty()) {
requestBuilder.addHeader("Authorization", "Bearer $token")
}
// ⚠️ 注意:這裡不再手動處理 401 logout,純粹放行交給 Authenticator 處理
chain.proceed(requestBuilder.build())
}
}
// 2. 配置 OkHttpClient,把 Interceptor 和 Authenticator 綁進去
@Provides
@Singleton
fun provideOkHttpClient(
tokenInterceptor: Interceptor,
tokenAuthenticator: TokenAuthenticator // 👈 注入前面實作好的 Authenticator
): OkHttpClient {
return OkHttpClient.Builder()
.connectTimeout(15, TimeUnit.SECONDS)
.readTimeout(15, TimeUnit.SECONDS)
.addInterceptor { chain ->
// 在 OkHttp 的背景執行緒中安全地進行同步等待
val (token, userId) = runBlocking(Dispatchers.IO) {
val t = tokenManager.currentAccessToken.first()
val id = tokenManager.currentUserId.first()
t to id
}
val requestBuilder = chain.request().newBuilder()
token?.let {
requestBuilder.addHeader("Authorization", "Bearer $it")
}
val response = chain.proceed(requestBuilder.build())
// 處理 Token 失效 (401)
if (response.code == 401) {
userId?.let { id ->
// 使用 Dispatchers.IO 確保不會卡死主執行緒
runBlocking(Dispatchers.IO) {
tokenManager.logout(id)
}
}
}
response
}
.addInterceptor(tokenInterceptor) // 負責幫一般請求加上 Token
.authenticator(tokenAuthenticator) // 負責在 401 時自動刷新 Token
.build()
}