Kotlin Ktor客户端身份验证功能不发送授权标头

Kotlin Ktor客户端身份验证功能不发送授权标头,kotlin,ktor,kotlin-multiplatform,ktor-client,Kotlin,Ktor,Kotlin Multiplatform,Ktor Client,我试图在Kotlin/MPP(多平台)项目中使用ktor,而在JVM目标上使用该特性似乎没有效果 下面是一个要复制的示例: import io.ktor.client.HttpClient 导入io.ktor.client.features.ResponseException 导入io.ktor.client.features.auth.auth 导入io.ktor.client.features.auth.providers.basic 导入io.ktor.client.features.js

我试图在Kotlin/MPP(多平台)项目中使用ktor,而在JVM目标上使用该特性似乎没有效果

下面是一个要复制的示例:
import io.ktor.client.HttpClient
导入io.ktor.client.features.ResponseException
导入io.ktor.client.features.auth.auth
导入io.ktor.client.features.auth.providers.basic
导入io.ktor.client.features.json.JsonFeature
导入io.ktor.client.features.json.serializer.KotlinxSerializer
导入io.ktor.client.features.logging.DEFAULT
导入io.ktor.client.features.logging.LogLevel
导入io.ktor.client.features.logging.Logger
导入io.ktor.client.features.logging.logging
导入io.ktor.client.request.get
导入io.ktor.client.request.header
导入kotlinx.coroutines.runBlocking
导入java.util*
fun main()=运行阻塞{
val client=HttpClient{
安装(日志记录){
logger=logger.DEFAULT
级别=LogLevel.HEADERS
}
安装(JsonFeature){
serializer=KotlinxSerializer()
}
安装(验证){
基本的{
用户名=“用户”
password=“pass”
}
}
}
val url=”https://en.wikipedia.org/wiki/Main_Page"
val失败=尝试{
client.get(url)
}捕获(e:响应异常){
“失败”
}
val=尝试{
client.get(url){
标头(“授权”,“基本${Base64.getEncoder().encodeToString”(“用户:pass.toByteArray())}”)
}
}捕获(e:响应异常){
“失败”
}
}
观察 从记录器输出中,您可以看到客户端没有发送
授权
头,但当我手动提供这样的头时,我没有遇到任何问题:

第一个请求(失败的示例:)

第二个请求(后续示例:)

环境
  • 科特林:1.4-M1
Ktor工件版本1.3.1:

  • ktor客户端核心
  • ktor客户端日志记录
  • ktor客户端json
  • ktor客户端序列化
  • ktor客户端身份验证基础

我遗漏了什么吗?请添加sendWithoutRequest=true

结果:

sending with sendWithoutRequest set to true
[main] INFO io.ktor.client.HttpClient - REQUEST: https://en.wikipedia.org/wiki/Main_Page
[main] INFO io.ktor.client.HttpClient - METHOD: HttpMethod(value=GET)
[main] INFO io.ktor.client.HttpClient - COMMON HEADERS
[main] INFO io.ktor.client.HttpClient - -> Authorization: Basic dXNlcjpwYXNz
[main] INFO io.ktor.client.HttpClient - -> Accept: application/json
[main] INFO io.ktor.client.HttpClient - -> Accept-Charset: UTF-8
[main] INFO io.ktor.client.HttpClient - CONTENT HEADERS
说明:

默认情况下,Ktor将等待服务器响应401, 未经授权,然后才发送身份验证标头。在你的 例如,wiki从不使用401响应,因为它不是受保护的 资源。因此,需要添加sendWithoutRequest。如果你 试着用一个响应401的url,你会发现 Ktor随后将发送第二个请求(在收到401之后),其中包含 身份验证标头。您可以尝试使用此url查看-

这是在关闭sendWithoutRequest的情况下对受保护的api执行的日志记录,即原始输入。如您所见,现在发出了2个请求,第一个请求没有授权头,第二个请求有授权头,在服务器用401响应之后

sending with sendWithoutRequest set to false and hitting a protected resource
    [main] INFO io.ktor.client.HttpClient - REQUEST: https://api.sumologic.com/api/v1/collectors
    [main] INFO io.ktor.client.HttpClient - METHOD: HttpMethod(value=GET)
    [main] INFO io.ktor.client.HttpClient - COMMON HEADERS
    [main] INFO io.ktor.client.HttpClient - -> Accept: application/json
    [main] INFO io.ktor.client.HttpClient - -> Accept-Charset: UTF-8
    [main] INFO io.ktor.client.HttpClient - CONTENT HEADERS
    [main] INFO io.ktor.client.HttpClient - REQUEST: https://api.sumologic.com/api/v1/collectors
    [main] INFO io.ktor.client.HttpClient - METHOD: HttpMethod(value=GET)
    [main] INFO io.ktor.client.HttpClient - COMMON HEADERS
    [main] INFO io.ktor.client.HttpClient - -> Accept: application/json
    [main] INFO io.ktor.client.HttpClient - -> Accept-Charset: UTF-8
    [main] INFO io.ktor.client.HttpClient - -> Authorization: Basic dXNlcjpwYXNz
    [main] INFO io.ktor.client.HttpClient - CONTENT HEADERS

注意:我刚刚看到Andrylamax的评论说新版本“修复”了它。也许,我不知道,因为我还没有尝试过这个新版本。但我想补充一点,这不是Ktor独有的东西,至少在这方面不是一个bug(但也许他们改变了主意?我也不知道)。事实上,正是我在C#方面的经验让我怀疑这里发生了什么,并找到了答案。C#中的WebRequest的行为与此相同,您需要将PreAuthenticate设置为true以立即发送凭据。看这里

不确定是否是这样,但这是一个固定的错误。尝试使用ktor版本1.3.5-M1I,我找不到这样的版本。你能推荐工件的来源吗?请原谅我的记忆,我混淆了协同程序版本和ktor版本。您应该使用ktor版本1.3.2-1.4-M1。因为它是用新后端编译的。1.3.1使用旧的backendAha!我知道我错过了什么。也许Ktor在线文档应该提到这一点。但奇怪的是,尽管我在问题中使用了Wikipedia URL,但实际上我确实使用了返回HTTP 401的端点,Ktor在第一次请求时立即抛出
ClientRequestException
,而未经授权退出。也许还缺少其他东西(我想知道你是否可以分享更多信息)。无论哪种方式,我都必须在每个请求上带有授权标题,这样你的答案就可以肯定地解决问题。
 install(Auth) {
            basic {
                sendWithoutRequest = true
                username = "user"
                password = "pass"
            }
        }
sending with sendWithoutRequest set to true
[main] INFO io.ktor.client.HttpClient - REQUEST: https://en.wikipedia.org/wiki/Main_Page
[main] INFO io.ktor.client.HttpClient - METHOD: HttpMethod(value=GET)
[main] INFO io.ktor.client.HttpClient - COMMON HEADERS
[main] INFO io.ktor.client.HttpClient - -> Authorization: Basic dXNlcjpwYXNz
[main] INFO io.ktor.client.HttpClient - -> Accept: application/json
[main] INFO io.ktor.client.HttpClient - -> Accept-Charset: UTF-8
[main] INFO io.ktor.client.HttpClient - CONTENT HEADERS
sending with sendWithoutRequest set to false and hitting a protected resource
    [main] INFO io.ktor.client.HttpClient - REQUEST: https://api.sumologic.com/api/v1/collectors
    [main] INFO io.ktor.client.HttpClient - METHOD: HttpMethod(value=GET)
    [main] INFO io.ktor.client.HttpClient - COMMON HEADERS
    [main] INFO io.ktor.client.HttpClient - -> Accept: application/json
    [main] INFO io.ktor.client.HttpClient - -> Accept-Charset: UTF-8
    [main] INFO io.ktor.client.HttpClient - CONTENT HEADERS
    [main] INFO io.ktor.client.HttpClient - REQUEST: https://api.sumologic.com/api/v1/collectors
    [main] INFO io.ktor.client.HttpClient - METHOD: HttpMethod(value=GET)
    [main] INFO io.ktor.client.HttpClient - COMMON HEADERS
    [main] INFO io.ktor.client.HttpClient - -> Accept: application/json
    [main] INFO io.ktor.client.HttpClient - -> Accept-Charset: UTF-8
    [main] INFO io.ktor.client.HttpClient - -> Authorization: Basic dXNlcjpwYXNz
    [main] INFO io.ktor.client.HttpClient - CONTENT HEADERS