Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Global/aos] Bearer Interceptor 구현 #110

Merged
merged 3 commits into from
Nov 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -23,7 +26,7 @@ object NetworkModule {
httpLoggingInterceptor: HttpLoggingInterceptor,
accessTokenInterceptor: AccessTokenInterceptor,
bearerInterceptor: BearerInterceptor
) : OkHttpClient {
): OkHttpClient {

return OkHttpClient.Builder()
.readTimeout(3000, TimeUnit.MILLISECONDS)
Expand All @@ -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)

Comment on lines +48 to +55
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

accesstokenInterceptor 없었는데 같이 추가 👍👍👍👍

@Provides
fun provideRetrofit(okHttpClient: OkHttpClient): Retrofit {

Expand All @@ -51,5 +63,5 @@ object NetworkModule {
.build()
}


}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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")
Expand Down
Original file line number Diff line number Diff line change
@@ -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 {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

사소한 컨밴션 이지만 이거 괄호 왜 썼는지?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

연산을 하려고 했는데 연산을 지우면서 같이 안 지운거같아 ㅋㅋㅋㅋㅋ 다음 커밋때 지울게요!

val newRequest = originalRequest.newBuilder()
.addHeader(AUTHORIZATION, "$BEARER $newAccessToken")
.build()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

기존 request 가 열려있으면 에러가 나서
여기서 response.close() 를 해줘야 정상적으로 새로운 request가 내려갔던 것으로 기억함!

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

같은 파일 41 줄에 response.close() 넣어뒀어! 어디 넣어하는건지 잘 몰라서 일단 재발급 성공한 직후에 넣어뒀는데 여기로 옮기는게 나을까?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

아! 못봤다ㅜㅜ 기존 위치가 좋은거 같아

return chain.proceed(newRequest)
}
}

return response
}


private suspend fun getNewAccessToken(refreshToken: String?): BaseState<BaseResponse<NaverLoginResponse>> {
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)) }
Comment on lines +72 to +78
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이 부분에 대해서 왜 retrofit을 di를 안했지? 라는 의문이 들었는데

    @Provides
    fun provideRetrofit(okHttpClient: OkHttpClient): Retrofit {

        return Retrofit.Builder()
            .baseUrl(BASE_URL)
            .client(okHttpClient)
            .addConverterFactory(GsonConverterFactory.create())
            .build()
    }

내가 이해한 바로는 retrofit -> okhttpclient -> bearerinterceptor -> retrofit 이렇게 순환참조가 일어나서 이렇게 선언을 했다고 생각하는데 생각하는 바가 맞는걸까?!

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

맞아!! 나도 di 로 하려고했는데 사이클이 돌아서 안되더라고..

}

companion object {
const val TOKEN_ERROR = 401
const val AUTHORIZATION = "Authorization"
const val BEARER = "Bearer"
}
Comment on lines +81 to +85
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이 부분 util패키지 -> constants에서 관리하고 있는데 거기다가 진행하는 거는 어떨까?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

constants 에 지금 상수값이 거의 다 들어가있어서 좀 나눠서 관리하는건 어떨까 싶어. 예를들어 Constant 라고만 하면 나중에 무슨 상수값인지 모르니까 값을 불러오려면 파일에 들어가서 확인해야하는데, Error, Network, Token 뭐 이런식으로 하면 쉽게 찾을 수 있을것 같아. 장단이 있을거 같은데 얘기해보면 좋을듯!

}


Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.avengers.nibobnebob.data.model.request

data class RefreshTokenRequest(
val refreshToken: String?
)
Original file line number Diff line number Diff line change
@@ -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<BaseResponse<NaverLoginResponse>>
@POST("api/auth/refresh-token")
suspend fun refreshToken(@Body refreshToken: RefreshTokenRequest) : Response<BaseResponse<NaverLoginResponse>>
}