You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Ktor Client获取Access Token后正确设置及即时请求无token问题解决

Ktor Client 运行时Token不携带问题解决方案

问题重现

我的Ktor Client配置如下:

@OptIn(ExperimentalSerializationApi::class)
@Singleton
@Provides
fun provideHttpClient(
    localService: LocalService,
): HttpClient {

    val chuckerInterceptor = ChuckerInterceptor.Builder(App.appContext)
        .collector(ChuckerCollector(App.appContext))
        .maxContentLength(250000L)
        .redactHeaders(emptySet())
        .alwaysReadResponseBody(false)
        .build()

    val okhttpEngine = OkHttp.create {
        addInterceptor(chuckerInterceptor)
    }
 

    return HttpClient(okhttpEngine) {

        install(ContentNegotiation) {
            json(
                Json {
                    prettyPrint = true
                    isLenient = true
                    useAlternativeNames = true
                    ignoreUnknownKeys = true
                    encodeDefaults = false
                    explicitNulls = false

                }
            )
        }

        install(HttpTimeout) {
            requestTimeoutMillis = 10_000
            connectTimeoutMillis = 10_000
            socketTimeoutMillis = 10_000
        }

        install(Logging) {
            logger = object : Logger {
                override fun log(message: String) {
                    Log.v("Logger Ktor =>", message)
                }
            }
            level = LogLevel.ALL
        }

        install(ResponseObserver) {
            onResponse { response ->
                Log.d("HTTP status:", "${response.status.value}")
            }
        }

        install(DefaultRequest) {
            header(HttpHeaders.ContentType, ContentType.Application.Json)
          
        }

        defaultRequest {
            contentType(ContentType.Application.Json)
            accept(ContentType.Application.Json)
          
        }

        install(Auth) {
            bearer {
                refreshTokens {
                    val token = client.get {
                        markAsRefreshTokenRequest()
                        url(BASE_URL + REFRESH_TOKEN)
                        parameter("refreshToken", localService.getRefreshToken())
                    }.body<AuthResponseDto>()
                    BearerTokens(
                        accessToken = token.accessToken.token,
                        refreshToken = token.refreshToken.token
                    )
                }
                loadTokens {
                    BearerTokens(
                        accessToken = localService.getAccessToken().orEmpty(),
                        refreshToken = localService.getRefreshToken().orEmpty(),
                    )
                }
            }
        }
    }
}

认证成功后,已将Access Token和Refresh Token保存到DataStore,但后续请求头里不携带Token;重启应用后请求又能正常携带Token。推测是Ktor无法在运行时从本地存储更新Client配置,求解决办法。

解决方案

问题核心是loadTokens默认仅在Client初始化时执行一次,后续不会主动重新读取本地存储的最新Token。按以下步骤调整即可解决:

1. 让loadTokens每次请求都读取最新Token

Ktor的Bearer认证中,loadTokens是一个可执行的lambda,每次需要Token时都会触发它——但前提是你的localService方法能实时返回最新值。

  • 检查LocalService的getAccessToken()和getRefreshToken()实现,确保它们不是缓存旧值,而是每次调用都直接从DataStore读取最新数据。
  • 如果DataStore的读取是挂起函数,需将loadTokens改成挂起形式:
loadTokens suspend {
    BearerTokens(
        accessToken = localService.getAccessToken().await().orEmpty(),
        refreshToken = localService.getRefreshToken().await().orEmpty()
    )
}

2. 保存Token后手动更新Client的Token

如果需要在保存Token后立即生效,可以在保存完成后手动设置BearerToken:

  • 初始化HttpClient时保留BearerAuth的引用:
val bearerAuth = install(Auth) {
    bearer {
        // 原有refreshTokens、loadTokens配置保持不变
    }
}
  • 在Token保存到DataStore后,调用:
bearerAuth.bearerTokens = BearerTokens(
    accessToken = newAccessToken,
    refreshToken = newRefreshToken
)

这会强制Client立即使用新Token,无需等待下次loadTokens调用。

3. 清理重复的DefaultRequest配置

你同时使用了install(DefaultRequest)和defaultRequest块,两者功能重复,建议保留其中一个即可,避免潜在的配置冲突。

内容的提问来源于stack exchange,提问作者Yarik Nikolenko

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.29 06:04:59