dm-181
This commit is contained in:
parent
89da510a3f
commit
f71cb04704
1 changed files with 41 additions and 28 deletions
|
|
@ -2,6 +2,7 @@ package com.example.login_v3.data.di
|
||||||
|
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import com.example.login_v3.data.api.TecnologiaApi
|
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.example.login_v3.data.repository.basic.TokenManager
|
||||||
import com.squareup.moshi.Moshi
|
import com.squareup.moshi.Moshi
|
||||||
import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory
|
import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory
|
||||||
|
|
@ -12,7 +13,9 @@ import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
import dagger.hilt.components.SingletonComponent
|
import dagger.hilt.components.SingletonComponent
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.flow.first
|
import kotlinx.coroutines.flow.first
|
||||||
|
import kotlinx.coroutines.flow.firstOrNull
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
|
import okhttp3.Interceptor
|
||||||
import okhttp3.OkHttpClient
|
import okhttp3.OkHttpClient
|
||||||
import retrofit2.Retrofit
|
import retrofit2.Retrofit
|
||||||
import retrofit2.converter.moshi.MoshiConverterFactory
|
import retrofit2.converter.moshi.MoshiConverterFactory
|
||||||
|
|
@ -32,39 +35,49 @@ object NetworkModule {
|
||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TokenInterceptor
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@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()
|
return OkHttpClient.Builder()
|
||||||
.connectTimeout(15, TimeUnit.SECONDS)
|
.connectTimeout(15, TimeUnit.SECONDS)
|
||||||
.readTimeout(15, TimeUnit.SECONDS)
|
.readTimeout(15, TimeUnit.SECONDS)
|
||||||
.addInterceptor { chain ->
|
.addInterceptor(tokenInterceptor) // 負責幫一般請求加上 Token
|
||||||
// 在 OkHttp 的背景執行緒中安全地進行同步等待
|
.authenticator(tokenAuthenticator) // 負責在 401 時自動刷新 Token
|
||||||
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
|
|
||||||
}
|
|
||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue