diff --git a/Aos/app/src/main/java/com/avengers/nibobnebob/app/di/NetworkModule.kt b/Aos/app/src/main/java/com/avengers/nibobnebob/app/di/NetworkModule.kt index 84c7188..7863d65 100644 --- a/Aos/app/src/main/java/com/avengers/nibobnebob/app/di/NetworkModule.kt +++ b/Aos/app/src/main/java/com/avengers/nibobnebob/app/di/NetworkModule.kt @@ -1,12 +1,15 @@ package com.avengers.nibobnebob.app.di +import android.content.Context import com.avengers.nibobnebob.BuildConfig +import com.avengers.nibobnebob.app.DataStoreManager import com.avengers.nibobnebob.config.AccessTokenInterceptor import com.avengers.nibobnebob.config.BearerInterceptor import com.avengers.nibobnebob.presentation.util.Constants.BASE_URL import dagger.Module import dagger.Provides import dagger.hilt.InstallIn +import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.components.SingletonComponent import okhttp3.OkHttpClient import okhttp3.logging.HttpLoggingInterceptor @@ -23,7 +26,7 @@ object NetworkModule { httpLoggingInterceptor: HttpLoggingInterceptor, accessTokenInterceptor: AccessTokenInterceptor, bearerInterceptor: BearerInterceptor - ) : OkHttpClient { + ): OkHttpClient { return OkHttpClient.Builder() .readTimeout(3000, TimeUnit.MILLISECONDS) @@ -37,10 +40,19 @@ object NetworkModule { @Provides fun provideLoggingInterceptor(): HttpLoggingInterceptor { return HttpLoggingInterceptor().apply { - level = if (BuildConfig.DEBUG) HttpLoggingInterceptor.Level.BODY else HttpLoggingInterceptor.Level.NONE + level = + if (BuildConfig.DEBUG) HttpLoggingInterceptor.Level.BODY else HttpLoggingInterceptor.Level.NONE } } + @Provides + fun provideAccessTokenInterceptor(dataStoreManager: DataStoreManager): AccessTokenInterceptor = + AccessTokenInterceptor(dataStoreManager) + + @Provides + fun provideBearerInterceptor(dataStoreManager: DataStoreManager): BearerInterceptor = + BearerInterceptor(dataStoreManager) + @Provides fun provideRetrofit(okHttpClient: OkHttpClient): Retrofit { @@ -51,5 +63,5 @@ object NetworkModule { .build() } - + } \ No newline at end of file diff --git a/Aos/app/src/main/java/com/avengers/nibobnebob/config/AccessTokenInterceptor.kt b/Aos/app/src/main/java/com/avengers/nibobnebob/config/AccessTokenInterceptor.kt index 7976b18..8e4e324 100644 --- a/Aos/app/src/main/java/com/avengers/nibobnebob/config/AccessTokenInterceptor.kt +++ b/Aos/app/src/main/java/com/avengers/nibobnebob/config/AccessTokenInterceptor.kt @@ -2,6 +2,7 @@ package com.avengers.nibobnebob.config import android.util.Log import com.avengers.nibobnebob.app.DataStoreManager +import com.avengers.nibobnebob.presentation.util.Constants.ACCESS import com.avengers.nibobnebob.presentation.util.Constants.AUTHORIZATION import com.avengers.nibobnebob.presentation.util.Constants.BEARER import kotlinx.coroutines.flow.first @@ -12,17 +13,16 @@ import okhttp3.Response import java.io.IOException import javax.inject.Inject -class AccessTokenInterceptor @Inject constructor(private val dataStoreManager: DataStoreManager) : - Interceptor { +class AccessTokenInterceptor @Inject constructor(private val dataStoreManager: DataStoreManager): Interceptor { @Throws(IOException::class) override fun intercept(chain: Interceptor.Chain): Response { // 동기가 아닌 비동기로 불러와야한다.. runBlocking말고 다른 방안에 대해 고민 - val accessToken = runBlocking { + val accessToken = runBlocking { dataStoreManager.getAccessToken().first() } - Log.d("토큰 테스트", accessToken.toString()) + Log.d("토큰 테스트",accessToken.toString()) val builder: Request.Builder = chain.request().newBuilder() accessToken?.takeIf { it.isNotEmpty() }?.let { builder.addHeader(AUTHORIZATION, "$BEARER $it") diff --git a/Aos/app/src/main/java/com/avengers/nibobnebob/config/BearerInterceptor.kt b/Aos/app/src/main/java/com/avengers/nibobnebob/config/BearerInterceptor.kt index 8c20059..47244ec 100644 --- a/Aos/app/src/main/java/com/avengers/nibobnebob/config/BearerInterceptor.kt +++ b/Aos/app/src/main/java/com/avengers/nibobnebob/config/BearerInterceptor.kt @@ -1,62 +1,88 @@ package com.avengers.nibobnebob.config +import com.avengers.nibobnebob.app.DataStoreManager +import com.avengers.nibobnebob.data.model.BaseState +import com.avengers.nibobnebob.data.model.request.RefreshTokenRequest +import com.avengers.nibobnebob.data.model.response.BaseResponse +import com.avengers.nibobnebob.data.model.response.NaverLoginResponse +import com.avengers.nibobnebob.data.model.runRemote +import com.avengers.nibobnebob.data.remote.RefreshApi +import com.avengers.nibobnebob.presentation.util.Constants +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.runBlocking import okhttp3.Interceptor +import okhttp3.OkHttpClient import okhttp3.Response +import okhttp3.logging.HttpLoggingInterceptor +import retrofit2.Retrofit +import retrofit2.converter.gson.GsonConverterFactory import java.io.IOException import javax.inject.Inject -class BearerInterceptor @Inject constructor() : Interceptor { +class BearerInterceptor @Inject constructor( + private val dataStoreManager: DataStoreManager +) : Interceptor { @Throws(IOException::class) override fun intercept(chain: Interceptor.Chain): Response { + val originalRequest = chain.request() val response = chain.proceed(originalRequest) -// runBlocking { -// 로컬에 refreshToken이 있다면 -// sharedPreferences.getString(X_REFRESH_TOKEN, null)?.let { refresh -> -// Log.d(TAG, refresh) -// // refresh API 호출 -// val result = Retrofit.Builder() -// .baseUrl(BASE_DEV_URL) -// .addConverterFactory(GsonConverterFactory.create()) -// .build() -// .create(RefreshAPI::class.java).refreshToken(refresh) -// -// if (result.isSuccessful) { -// Log.d(TAG,"리프래시 성공") -// result.body()?.let { body -> -// Log.d(TAG,body.accessToken) -// // refresh 성공시 로컬에 저장 -// sharedPreferences.edit() -// .putString(X_ACCESS_TOKEN, body.accessToken) -// .putString(X_REFRESH_TOKEN, body.refreshToken) -// .apply() -// -// isRefreshed = true -// accessToken = body.accessToken -// } -// }else{ -// val error = -// Gson().fromJson(result.errorBody()?.string(), ErrorResponse::class.java) -// Log.d(TAG,error.message) -// } -// } -// } -// -// if (isRefreshed) { -// -// // 기존 API 재호출 -// val newRequest = originalRequest.newBuilder() -// .addHeader("Authorization", accessToken) -// .build() -// -// return chain.proceed(newRequest) -// } - // 해당 특정 에러코드가 그대로 내려간다면, 세션 만료 처리 + var newAccessToken: String? = null + + if (response.code == TOKEN_ERROR) { + runBlocking { + val refreshToken = dataStoreManager.getRefreshToken().first() + refreshToken?.let { token -> + when (val result = getNewAccessToken(token)) { + is BaseState.Success -> { + response.close() + newAccessToken = result.data.body.accessToken + newAccessToken?.let { + dataStoreManager.putAccessToken(newAccessToken!!) + } + } + + else -> { + dataStoreManager.deleteAccessToken() + dataStoreManager.deleteRefreshToken() + } + } + } + } + (newAccessToken)?.let { + val newRequest = originalRequest.newBuilder() + .addHeader(AUTHORIZATION, "$BEARER $newAccessToken") + .build() + return chain.proceed(newRequest) + } + } + return response } + + + private suspend fun getNewAccessToken(refreshToken: String?): BaseState> { + val loggingInterceptor = HttpLoggingInterceptor() + loggingInterceptor.level = HttpLoggingInterceptor.Level.BODY + val okHttpClient = OkHttpClient.Builder().addInterceptor(loggingInterceptor).build() + + val retrofit = Retrofit.Builder() + .baseUrl(Constants.BASE_URL) + .addConverterFactory(GsonConverterFactory.create()) + .client(okHttpClient) + .build() + val api = retrofit.create(RefreshApi::class.java) + return runRemote { api.refreshToken(RefreshTokenRequest(refreshToken)) } + } + + companion object { + const val TOKEN_ERROR = 401 + const val AUTHORIZATION = "Authorization" + const val BEARER = "Bearer" + } } diff --git a/Aos/app/src/main/java/com/avengers/nibobnebob/data/model/request/RefreshTokenRequest.kt b/Aos/app/src/main/java/com/avengers/nibobnebob/data/model/request/RefreshTokenRequest.kt new file mode 100644 index 0000000..e250ba6 --- /dev/null +++ b/Aos/app/src/main/java/com/avengers/nibobnebob/data/model/request/RefreshTokenRequest.kt @@ -0,0 +1,5 @@ +package com.avengers.nibobnebob.data.model.request + +data class RefreshTokenRequest( + val refreshToken: String? +) \ No newline at end of file diff --git a/Aos/app/src/main/java/com/avengers/nibobnebob/data/remote/RefreshApi.kt b/Aos/app/src/main/java/com/avengers/nibobnebob/data/remote/RefreshApi.kt index 498cd40..8bcb828 100644 --- a/Aos/app/src/main/java/com/avengers/nibobnebob/data/remote/RefreshApi.kt +++ b/Aos/app/src/main/java/com/avengers/nibobnebob/data/remote/RefreshApi.kt @@ -1,12 +1,14 @@ package com.avengers.nibobnebob.data.remote +import com.avengers.nibobnebob.data.model.request.RefreshTokenRequest import com.avengers.nibobnebob.data.model.response.BaseResponse import com.avengers.nibobnebob.data.model.response.NaverLoginResponse import retrofit2.Response +import retrofit2.http.Body import retrofit2.http.POST interface RefreshApi { - @POST("토큰 갱신 url") - suspend fun refreshToken(refreshToken : String) : Response> + @POST("api/auth/refresh-token") + suspend fun refreshToken(@Body refreshToken: RefreshTokenRequest) : Response> } \ No newline at end of file