feat: 添加 Ktor 客户端认证插件以增强 API 安全性
- 在 build.gradle.kts 中新增 Ktor 客户端认证依赖。 - 更新 ApiService 类,使用 Ktor 内置的 Auth 插件实现 Bearer token 自动管理和刷新逻辑,提升 API 调用的安全性和稳定性。
This commit is contained in:
@@ -137,6 +137,7 @@ dependencies {
|
||||
implementation(libs.ktor.client.content.negotiation)
|
||||
implementation(libs.ktor.serialization.kotlinx.json)
|
||||
implementation(libs.ktor.client.logging)
|
||||
implementation(libs.ktor.client.auth)
|
||||
|
||||
// Room
|
||||
implementation(libs.androidx.room.runtime)
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
package com.huaga.life_echo.network
|
||||
|
||||
import com.huaga.life_echo.data.auth.TokenManager
|
||||
import com.huaga.life_echo.network.interceptors.AuthInterceptorPlugin
|
||||
import com.huaga.life_echo.network.models.*
|
||||
import io.ktor.client.*
|
||||
import io.ktor.client.call.*
|
||||
import io.ktor.client.engine.android.*
|
||||
import io.ktor.client.plugins.auth.*
|
||||
import io.ktor.client.plugins.auth.providers.*
|
||||
import io.ktor.client.plugins.contentnegotiation.*
|
||||
import io.ktor.client.plugins.logging.*
|
||||
import io.ktor.client.plugins.HttpTimeout
|
||||
@@ -19,8 +20,8 @@ import kotlinx.serialization.json.jsonObject
|
||||
import kotlinx.serialization.json.jsonPrimitive
|
||||
|
||||
class ApiService(
|
||||
tokenManager: TokenManager? = null,
|
||||
authService: AuthService? = null
|
||||
private val tokenManager: TokenManager? = null,
|
||||
private val authService: AuthService? = null
|
||||
) {
|
||||
private val client = HttpClient(Android) {
|
||||
install(ContentNegotiation) {
|
||||
@@ -37,11 +38,48 @@ class ApiService(
|
||||
socketTimeoutMillis = 45_000 // 读写超时
|
||||
}
|
||||
|
||||
// 如果提供了tokenManager和authService,安装认证拦截器
|
||||
// 使用Ktor内置Auth插件:自动添加Bearer token,401时自动刷新并重试
|
||||
if (tokenManager != null && authService != null) {
|
||||
install(AuthInterceptorPlugin) {
|
||||
this.tokenManager = tokenManager
|
||||
this.authService = authService
|
||||
install(Auth) {
|
||||
bearer {
|
||||
loadTokens {
|
||||
val access = tokenManager.getAccessToken()
|
||||
val refresh = tokenManager.getRefreshToken()
|
||||
if (!access.isNullOrBlank() && !refresh.isNullOrBlank()) {
|
||||
BearerTokens(access, refresh)
|
||||
} else null
|
||||
}
|
||||
refreshTokens {
|
||||
val refresh = oldTokens?.refreshToken
|
||||
?: tokenManager.getRefreshToken()
|
||||
if (!refresh.isNullOrBlank()) {
|
||||
val result = authService.refreshToken(refresh)
|
||||
result.fold(
|
||||
onSuccess = { tokenResponse ->
|
||||
tokenManager.saveTokens(
|
||||
tokenResponse.access_token,
|
||||
tokenResponse.refresh_token,
|
||||
tokenManager.getUserId() ?: ""
|
||||
)
|
||||
BearerTokens(
|
||||
tokenResponse.access_token,
|
||||
tokenResponse.refresh_token
|
||||
)
|
||||
},
|
||||
onFailure = {
|
||||
tokenManager.clearTokens()
|
||||
tokenManager.notifyTokenRefreshFailed()
|
||||
null
|
||||
}
|
||||
)
|
||||
} else {
|
||||
tokenManager.clearTokens()
|
||||
tokenManager.notifyTokenRefreshFailed()
|
||||
null
|
||||
}
|
||||
}
|
||||
sendWithoutRequest { true }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,6 +45,7 @@ ktor-client-websockets = { group = "io.ktor", name = "ktor-client-websockets", v
|
||||
ktor-client-content-negotiation = { group = "io.ktor", name = "ktor-client-content-negotiation", version.ref = "ktor" }
|
||||
ktor-serialization-kotlinx-json = { group = "io.ktor", name = "ktor-serialization-kotlinx-json", version.ref = "ktor" }
|
||||
ktor-client-logging = { group = "io.ktor", name = "ktor-client-logging", version.ref = "ktor" }
|
||||
ktor-client-auth = { group = "io.ktor", name = "ktor-client-auth", version.ref = "ktor" }
|
||||
|
||||
# Room
|
||||
androidx-room-runtime = { group = "androidx.room", name = "room-runtime", version.ref = "room" }
|
||||
|
||||
Reference in New Issue
Block a user