更新 token 刷新

新增 token 刷新功能,包括 API 接口和拦截器逻辑。
This commit is contained in:
2024-08-31 23:49:15 +08:00
parent 98430f3282
commit 091926a3c1
6 changed files with 66 additions and 25 deletions

View File

@@ -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")
}

View File

@@ -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"
}

View File

@@ -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<TrustManager>(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()

View File

@@ -89,6 +89,11 @@ interface RiderProAPI {
@GET("auth/token")
suspend fun checkToken(): Response<ValidateTokenResult>
@GET("auth/refresh_token")
suspend fun refreshToken(
@Query("token") token: String
): Response<AuthResult>
@GET("posts")
suspend fun getPosts(
@Query("page") page: Int = 1,

View File

@@ -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<Bitmap?>(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,

View File

@@ -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)