From 091926a3c102ceef22150c206c55eb0b5a4e2fda Mon Sep 17 00:00:00 2001 From: AllenTom Date: Sat, 31 Aug 2024 23:49:15 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=96=B0=20token=20=E5=88=B7=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 新增 token 刷新功能,包括 API 接口和拦截器逻辑。 --- app/build.gradle.kts | 1 + .../main/java/com/aiosman/riderpro/Const.kt | 3 +- .../aiosman/riderpro/data/api/ApiClient.kt | 51 +++++++++++++++++-- .../aiosman/riderpro/data/api/RiderProAPI.kt | 5 ++ .../aiosman/riderpro/ui/composables/Image.kt | 28 ++++------ .../java/com/aiosman/riderpro/utils/Utils.kt | 3 +- 6 files changed, 66 insertions(+), 25 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 100dc1f..daa59c4 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -90,5 +90,6 @@ dependencies { implementation("com.squareup.retrofit2:converter-gson:2.11.0") implementation("androidx.credentials:credentials:1.2.2") implementation("androidx.credentials:credentials-play-services-auth:1.2.2") + implementation("com.auth0.android:jwtdecode:2.0.2") } diff --git a/app/src/main/java/com/aiosman/riderpro/Const.kt b/app/src/main/java/com/aiosman/riderpro/Const.kt index 1dd555b..101dd9b 100644 --- a/app/src/main/java/com/aiosman/riderpro/Const.kt +++ b/app/src/main/java/com/aiosman/riderpro/Const.kt @@ -2,6 +2,7 @@ package com.aiosman.riderpro object ConstVars { // api 地址 -// const val BASE_SERVER = "http://192.168.31.250:8088" +// const val BASE_SERVER = "http://192.168.31.190:8088" +// const val BASE_SERVER = "http://192.168.31.251:8088" const val BASE_SERVER = "https://8.137.22.101:8088" } \ No newline at end of file diff --git a/app/src/main/java/com/aiosman/riderpro/data/api/ApiClient.kt b/app/src/main/java/com/aiosman/riderpro/data/api/ApiClient.kt index fef6181..368b045 100644 --- a/app/src/main/java/com/aiosman/riderpro/data/api/ApiClient.kt +++ b/app/src/main/java/com/aiosman/riderpro/data/api/ApiClient.kt @@ -4,6 +4,9 @@ import android.icu.text.SimpleDateFormat import android.icu.util.TimeZone import com.aiosman.riderpro.AppStore import com.aiosman.riderpro.ConstVars +import com.aiosman.riderpro.data.ServiceException +import com.auth0.android.jwt.JWT +import kotlinx.coroutines.runBlocking import okhttp3.Interceptor import okhttp3.OkHttpClient import okhttp3.Response @@ -16,7 +19,9 @@ import javax.net.ssl.SSLContext import javax.net.ssl.TrustManager import javax.net.ssl.X509TrustManager -fun getUnsafeOkHttpClient(): OkHttpClient { +fun getUnsafeOkHttpClient( + authInterceptor: AuthInterceptor? = null +): OkHttpClient { return try { // Create a trust manager that does not validate certificate chains val trustAllCerts = arrayOf(object : X509TrustManager { @@ -46,7 +51,11 @@ fun getUnsafeOkHttpClient(): OkHttpClient { OkHttpClient.Builder() .sslSocketFactory(sslSocketFactory, trustAllCerts[0] as X509TrustManager) .hostnameVerifier { _, _ -> true } - .addInterceptor(AuthInterceptor()) + .apply { + authInterceptor?.let { + addInterceptor(it) + } + } .build() } catch (e: Exception) { throw RuntimeException(e) @@ -56,8 +65,42 @@ fun getUnsafeOkHttpClient(): OkHttpClient { class AuthInterceptor() : Interceptor { override fun intercept(chain: Interceptor.Chain): Response { val requestBuilder = chain.request().newBuilder() + val token = AppStore.token + token?.let { + val jwt = JWT(token) + val expiresAt = jwt.expiresAt?.time?.minus(3000) + val currentTime = System.currentTimeMillis() + val isExpired = expiresAt != null && currentTime > expiresAt + if (isExpired) { + runBlocking { + val newToken = refreshToken() + if (newToken != null) { + AppStore.token = newToken + } + } + } + } + requestBuilder.addHeader("Authorization", "Bearer ${AppStore.token}") - return chain.proceed(requestBuilder.build()) + + val response = chain.proceed(requestBuilder.build()) + return response + } + + private suspend fun refreshToken(): String? { + val client = Retrofit.Builder() + .baseUrl(ApiClient.RETROFIT_URL) + .addConverterFactory(GsonConverterFactory.create()) + .client(getUnsafeOkHttpClient()) + .build() + .create(RiderProAPI::class.java) + + val resp = client.refreshToken(AppStore.token ?: "") + val newToken = resp.body()?.token + if (newToken != null) { + AppStore.token = newToken + } + return newToken } } @@ -67,7 +110,7 @@ object ApiClient { const val RETROFIT_URL = "${BASE_API_URL}/" const val TIME_FORMAT = "yyyy-MM-dd HH:mm:ss" private val okHttpClient: OkHttpClient by lazy { - getUnsafeOkHttpClient() + getUnsafeOkHttpClient(authInterceptor = AuthInterceptor()) } private val retrofit: Retrofit by lazy { Retrofit.Builder() diff --git a/app/src/main/java/com/aiosman/riderpro/data/api/RiderProAPI.kt b/app/src/main/java/com/aiosman/riderpro/data/api/RiderProAPI.kt index e7e2133..3f6a437 100644 --- a/app/src/main/java/com/aiosman/riderpro/data/api/RiderProAPI.kt +++ b/app/src/main/java/com/aiosman/riderpro/data/api/RiderProAPI.kt @@ -89,6 +89,11 @@ interface RiderProAPI { @GET("auth/token") suspend fun checkToken(): Response + @GET("auth/refresh_token") + suspend fun refreshToken( + @Query("token") token: String + ): Response + @GET("posts") suspend fun getPosts( @Query("page") page: Int = 1, diff --git a/app/src/main/java/com/aiosman/riderpro/ui/composables/Image.kt b/app/src/main/java/com/aiosman/riderpro/ui/composables/Image.kt index 7a3e57b..27487da 100644 --- a/app/src/main/java/com/aiosman/riderpro/ui/composables/Image.kt +++ b/app/src/main/java/com/aiosman/riderpro/ui/composables/Image.kt @@ -56,15 +56,15 @@ fun CustomAsyncImage( contentScale: ContentScale = ContentScale.Crop ) { val imageLoader = getImageLoader(context) - val blurBitmap = remember(blurHash) { - blurHash?.let { - BlurHashDecoder.decode( - blurHash = it, - width = DEFAULT_HASHED_BITMAP_WIDTH, - height = DEFAULT_HASHED_BITMAP_HEIGHT - ) - } - } +// val blurBitmap = remember(blurHash) { +// blurHash?.let { +// BlurHashDecoder.decode( +// blurHash = it, +// width = DEFAULT_HASHED_BITMAP_WIDTH, +// height = DEFAULT_HASHED_BITMAP_HEIGHT +// ) +// } +// } // var bitmap by remember(imageUrl) { mutableStateOf(null) } // @@ -86,16 +86,6 @@ fun CustomAsyncImage( model = ImageRequest.Builder(context) .data(imageUrl) .crossfade(200) - .apply { - if (placeholderRes != null) { - placeholder(placeholderRes) - return@apply - } - if (blurBitmap != null) { - placeholder(blurBitmap.toDrawable(context.resources)) - } - - } .build(), contentDescription = contentDescription, modifier = modifier, diff --git a/app/src/main/java/com/aiosman/riderpro/utils/Utils.kt b/app/src/main/java/com/aiosman/riderpro/utils/Utils.kt index 54d2e62..1d749b0 100644 --- a/app/src/main/java/com/aiosman/riderpro/utils/Utils.kt +++ b/app/src/main/java/com/aiosman/riderpro/utils/Utils.kt @@ -5,6 +5,7 @@ import coil.ImageLoader import coil.disk.DiskCache import coil.memory.MemoryCache import coil.request.CachePolicy +import com.aiosman.riderpro.data.api.AuthInterceptor import com.aiosman.riderpro.data.api.getUnsafeOkHttpClient import java.util.Date import java.util.concurrent.TimeUnit @@ -18,7 +19,7 @@ object Utils { } fun getImageLoader(context: Context): ImageLoader { - val okHttpClient = getUnsafeOkHttpClient() + val okHttpClient = getUnsafeOkHttpClient(authInterceptor = AuthInterceptor()) return ImageLoader.Builder(context) .okHttpClient(okHttpClient) .memoryCachePolicy(CachePolicy.ENABLED)