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

[Feat/#55] token interceptor + accessToken 재발급 + token 컨트롤 #63

Merged
merged 9 commits into from
Jan 10, 2024
72 changes: 72 additions & 0 deletions app/src/main/java/com/going/doorip/di/AuthInterceptor.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package com.going.doorip.di

import android.content.Context
import com.going.data.dto.BaseResponse
import com.going.data.local.GoingDataStore
import com.going.domain.entity.response.AuthTokenModel
import com.going.doorip.BuildConfig.BASE_URL
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.serialization.json.Json
import okhttp3.Interceptor
import okhttp3.Request
import okhttp3.RequestBody.Companion.toRequestBody
import okhttp3.Response
import timber.log.Timber
import javax.inject.Inject

class AuthInterceptor @Inject constructor(
private val json: Json,
private val dataStore: GoingDataStore,
@ApplicationContext private val context: Context,
) : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
Comment on lines +21 to +22
Copy link
Member

Choose a reason for hiding this comment

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

줄바꿈하면 더 예쁠

val originalRequest = chain.request()

Timber.d("GET ACCESS TOKEN : ${dataStore.accessToken}")

val authRequest = if (dataStore.accessToken.isNotBlank()) {
originalRequest.newAuthBuilder().build()
} else {
originalRequest
}
val response = chain.proceed(authRequest)

when (response.code) {
CODE_TOKEN_EXPIRED -> {
try {
val refreshTokenRequest = originalRequest.newBuilder().post("".toRequestBody())
.url("$BASE_URL/api/users/reissue")
.addHeader(AUTHORIZATION, dataStore.refreshToken)
.build()
val refreshTokenResponse = chain.proceed(refreshTokenRequest)
Timber.d("GET REFRESH TOKEN : $refreshTokenResponse")

if (refreshTokenResponse.isSuccessful) {
val responseToken = json.decodeFromString(
refreshTokenResponse.body?.string().toString(),
) as BaseResponse<AuthTokenModel>

with(dataStore) {
accessToken = responseToken.data.accessToken
refreshToken = responseToken.data.refreshToken
}
refreshTokenResponse.close()
val newRequest = originalRequest.newAuthBuilder().build()
return chain.proceed(newRequest)
}
} catch (t: Throwable) {
Timber.e(t)
}
}
}
return response
}

private fun Request.newAuthBuilder() =
this.newBuilder().addHeader(AUTHORIZATION, "Bearer ${dataStore.accessToken}")

companion object {
private const val CODE_TOKEN_EXPIRED = 401
private const val AUTHORIZATION = "Authorization"
}
}
7 changes: 7 additions & 0 deletions app/src/main/java/com/going/doorip/di/DataSourceModule.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@ package com.going.doorip.di

import com.going.data.datasource.AuthDataSource
import com.going.data.datasource.MockDataSource
import com.going.data.datasource.SettingDataSource
import com.going.data.datasourceImpl.AuthDataSourceImpl
import com.going.data.datasourceImpl.MockDataSourceImpl
import com.going.data.datasourceImpl.SettingDataSourceImpl
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
Expand All @@ -23,4 +25,9 @@ object DataSourceModule {
@Singleton
fun provideAuthDataSource(authDataSourceImpl: AuthDataSourceImpl): AuthDataSource =
authDataSourceImpl

@Provides
@Singleton
fun provideSettingDataSource(settingDataSourceImpl: SettingDataSourceImpl): SettingDataSource =
settingDataSourceImpl
}
2 changes: 1 addition & 1 deletion app/src/main/java/com/going/doorip/di/DataStoreModule.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ package com.going.doorip.di

import android.content.Context
import android.content.SharedPreferences
import com.going.data.local.GoingDataStoreImpl
import com.going.data.local.GoingDataStore
import com.going.data.local.GoingDataStoreImpl
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
Expand Down
11 changes: 9 additions & 2 deletions app/src/main/java/com/going/doorip/di/RepositoryModule.kt
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
package com.going.doorip.di

import com.going.data.repositoryImpl.TokenRepositoryImpl
import com.going.data.repositoryImpl.AuthRepositoryImpl
import com.going.data.repositoryImpl.MockRepositoryImpl
import com.going.domain.repository.TokenRepository
import com.going.data.repositoryImpl.SettingRepositoryImpl
import com.going.data.repositoryImpl.TokenRepositoryImpl
import com.going.domain.repository.AuthRepository
import com.going.domain.repository.MockRepository
import com.going.domain.repository.SettingRepository
import com.going.domain.repository.TokenRepository
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
Expand All @@ -30,4 +32,9 @@ object RepositoryModule {
@Singleton
fun provideTokenRepository(tokenRepositoryImpl: TokenRepositoryImpl): TokenRepository =
tokenRepositoryImpl

@Provides
@Singleton
fun provideSettingRepository(settingRepositoryImpl: SettingRepositoryImpl): SettingRepository =
settingRepositoryImpl
}
13 changes: 11 additions & 2 deletions app/src/main/java/com/going/doorip/di/RetrofitModule.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package com.going.doorip.di

import com.going.doorip.BuildConfig.BASE_URL
import com.going.going.di.AuthInterceptor
import com.going.going.di.qualifier.JWT
import com.jakewharton.retrofit2.converter.kotlinx.serialization.asConverterFactory
import dagger.Module
import dagger.Provides
Expand Down Expand Up @@ -54,15 +56,22 @@ object RetrofitModule {

@Provides
@Singleton
fun provideOkHttpClient(
@JWT
fun provideAuthInterceptor(authInterceptor: AuthInterceptor): Interceptor = authInterceptor

@Provides
@Singleton
fun provideJWTOkHttpClient(
loggingInterceptor: Interceptor,
@JWT authInterceptor: Interceptor,
): OkHttpClient = OkHttpClient.Builder()
.addInterceptor(loggingInterceptor)
.addInterceptor(authInterceptor)
.build()

Comment on lines 57 to 71
Copy link
Contributor

Choose a reason for hiding this comment

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

그는 신이다

@Provides
@Singleton
fun provideRetrofit(
fun provideJWTRetrofit(
client: OkHttpClient,
factory: Converter.Factory,
): Retrofit = Retrofit.Builder()
Expand Down
8 changes: 7 additions & 1 deletion app/src/main/java/com/going/doorip/di/ServiceModule.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package com.going.doorip.di

import com.going.data.service.AuthService
import com.going.data.service.MockService
import com.going.data.service.SettingService
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
Expand All @@ -20,6 +21,11 @@ object ServiceModule {

@Provides
@Singleton
fun provideLoginService(retrofit: Retrofit): AuthService =
fun provideAuthService(retrofit: Retrofit): AuthService =
retrofit.create(AuthService::class.java)

@Provides
@Singleton
fun provideSettingService(retrofit: Retrofit): SettingService =
retrofit.create(SettingService::class.java)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.going.doorip.di.qualifier

import javax.inject.Qualifier

@Qualifier
@Retention(AnnotationRetention.BINARY)
annotation class JWT
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.going.data.datasource

import com.going.data.dto.response.SignOutResponseDto

interface SettingDataSource {
suspend fun patchSignOut(): SignOutResponseDto
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.going.data.datasourceImpl

import com.going.data.datasource.SettingDataSource
import com.going.data.dto.response.SignOutResponseDto
import com.going.data.service.SettingService
import javax.inject.Inject

class SettingDataSourceImpl @Inject constructor(
private val settingService: SettingService,
) : SettingDataSource {
override suspend fun patchSignOut(): SignOutResponseDto = settingService.patchSignOut()
}
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
package com.going.data.dto.request

import com.going.domain.entity.request.RequestSignInModel
import com.going.domain.entity.request.RequestSignUpModel
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

@Serializable
data class SignInRequestDto(
@SerialName("platform")
val platform: String
val platform: String,
)

fun RequestSignInModel.toSignInRequestDto(): SignInRequestDto =
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.going.data.dto.response

import com.going.domain.entity.response.SignOutModel
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

@Serializable
data class SignOutResponseDto(
@SerialName("status")
val status: Int,
@SerialName("message")
val message: String,
) {
fun toSignOutModel() =
SignOutModel(status, message)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.going.data.repositoryImpl

import com.going.data.datasource.SettingDataSource
import com.going.domain.entity.response.SignOutModel
import com.going.domain.repository.SettingRepository
import javax.inject.Inject

class SettingRepositoryImpl @Inject constructor(
private val settingDataSource: SettingDataSource,
) : SettingRepository {
override suspend fun patchSignOut(): Result<SignOutModel> = runCatching {
settingDataSource.patchSignOut().toSignOutModel()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,15 @@ class TokenRepositoryImpl @Inject constructor(
private val goingDataStore: GoingDataStore,
) : TokenRepository {
override fun getAccessToken(): String = goingDataStore.accessToken
override fun getRefreshToken(): String = goingDataStore.refreshToken

override fun setTokens(accessToken: String, refreshToken: String) {
goingDataStore.accessToken = accessToken
goingDataStore.refreshToken = refreshToken
}

override fun getRefreshToken(): String = goingDataStore.refreshToken
override fun clearTokens() {
goingDataStore.accessToken = ""
goingDataStore.refreshToken = ""
}
}
9 changes: 9 additions & 0 deletions data/src/main/java/com/going/data/service/SettingService.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.going.data.service

import com.going.data.dto.response.SignOutResponseDto
import retrofit2.http.PATCH

interface SettingService {
@PATCH("api/users/signout")
suspend fun patchSignOut(): SignOutResponseDto
Comment on lines +6 to +8
Copy link
Member

Choose a reason for hiding this comment

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

PATCH 저도 사용해야 하는데 참고하겠습니당

Copy link
Contributor

Choose a reason for hiding this comment

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

저두욤

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.going.domain.entity.response

data class SignOutModel(
val status: Int,
val message: String,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.going.domain.repository

import com.going.domain.entity.response.SignOutModel

interface SettingRepository {
suspend fun patchSignOut(): Result<SignOutModel>
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@ interface TokenRepository {
fun getRefreshToken(): String

fun setTokens(accessToken: String, refreshToken: String)
fun clearTokens()
Comment on lines 8 to +9
Copy link
Member

Choose a reason for hiding this comment

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

줄바꿈 해주실 수 있나요 ㅋㅋ

}
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,7 @@ import javax.inject.Inject
class SplashViewModel @Inject constructor(
private val tokenRepository: TokenRepository,
) : ViewModel() {
fun getHasAccessToken(): Boolean = tokenRepository.getAccessToken() != ""
fun getHasAccessToken(): Boolean = tokenRepository.getAccessToken().isNotBlank()

fun clearTokens() = tokenRepository.clearTokens()
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.going.presentation.tendencytest.result

import android.media.SubtitleData
import android.os.Bundle
import android.text.SpannableString
import android.text.Spanned
Expand All @@ -26,7 +27,7 @@ class TendencyTestResultActivity :
with(binding) {
tvTendencyTestResultTitle.text = getString(R.string.tendency_test_result_title, "찐두릅")

viewModel?.mockTendencyResult?.apply {
viewModel.mockTendencyResult.apply {
Copy link
Member

Choose a reason for hiding this comment

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

수정 조하요

tvTendencyTestResultType.text = profileTitle
tvTendencyTestResultSubType.text = profileSubTitle

Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
package com.going.presentation.util

import android.util.Log
import org.json.JSONObject
import retrofit2.HttpException

fun toErrorCode(throwable: Throwable): String = if (throwable is HttpException) {
val jsonTemp = throwable.response()?.errorBody()?.byteString().toString()
val json = jsonTemp.slice(6 until jsonTemp.length)
Log.e("TAG", "toErrorCode: $jsonTemp", )
val json = jsonTemp.slice(6 until jsonTemp.length - 2) + "}"
Log.e("TAG", "toErrorCode: $json", )
JSONObject(json).getString("code")
} else {
"NOT_HTTP"
}

// 기능 오류 발견!!!! 반드시 수정 필요!!!!!!
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,7 @@
xmlns:app="http://schemas.android.com/apk/res-auto">

<data>
<variable
name="viewModel"
type="com.going.presentation.tendencytest.result.TendencyTestResultViewModel" />

</data>

<androidx.constraintlayout.widget.ConstraintLayout
Expand Down