Функция аутентификации клиента Ktor не отправляет заголовок авторизации - PullRequest
1 голос
/ 22 апреля 2020

Я пытаюсь использовать ktor client в проекте Kotlin / MPP (Multiplatform) и в целевой функции JVM basi c аутентификация , похоже, не имеют эффект.

Вот пример для воспроизведения:

import io.ktor.client.HttpClient
import io.ktor.client.features.ResponseException
import io.ktor.client.features.auth.Auth
import io.ktor.client.features.auth.providers.basic
import io.ktor.client.features.json.JsonFeature
import io.ktor.client.features.json.serializer.KotlinxSerializer
import io.ktor.client.features.logging.DEFAULT
import io.ktor.client.features.logging.LogLevel
import io.ktor.client.features.logging.Logger
import io.ktor.client.features.logging.Logging
import io.ktor.client.request.get
import io.ktor.client.request.header
import kotlinx.coroutines.runBlocking
import java.util.*

fun main() = runBlocking {
    val client = HttpClient {
        install(Logging) {
            logger = Logger.DEFAULT
            level = LogLevel.HEADERS
        }
        install(JsonFeature) {
            serializer = KotlinxSerializer()
        }
        install(Auth) {
            basic {
                username = "user"
                password = "pass"
            }
        }
    }
    val url = "https://en.wikipedia.org/wiki/Main_Page"

    val failing = try {
        client.get<String>(url)
    } catch (e: ResponseException) {
        "failed"
    }

    val succeeding = try {
        client.get<String>(url) {
            header("Authorization", "Basic ${Base64.getEncoder().encodeToString("user:pass".toByteArray())}")
        }
    } catch (e: ResponseException) {
        "failed"
    }
}

Наблюдение

Из выходных данных регистратора видно, что клиент не отправляет заголовок Authorization но у меня нет проблем, когда я предоставляю такой заголовок вручную:

Первый запрос (ошибочный пример:)

[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 - -> 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://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

Среда

  • Kotlin: 1.4-M1

Ktor Artifacts версия 1.3.1:

  • ktor-client-core
  • ktor-client-logging
  • ktor-client- json
  • ktor-client-serialization
  • ktor-client-auth-basi c

Я что-то пропустил?

1 Ответ

1 голос
/ 26 апреля 2020

Пожалуйста, добавьте sendWithoutRequest = true

https://api.ktor.io/1.3.1/io.ktor.client.features.auth.providers/-basic-auth-config/send-without-request.html

 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

Объяснение:

По умолчанию Ktor будет ожидать ответа от сервера 401, Unauthorized, и только затем отправит заголовок аутентификации. В вашем примере вики никогда не отвечает 401, так как это не защищенный ресурс. Поэтому добавление sendWithoutRequest является обязательным. Если вы попытались использовать URL-адрес, который отвечает 401, вы увидите, что Ktor затем отправит второй запрос (после получения 401) с заголовком аутентификации. Вы можете попробовать с помощью этого URL увидеть - https://api.sumologic.com/api/v1/collectors.

Это запись в журнал, когда выполняется для этого защищенного API с отключенным sendWithoutRequest, ваш исходный ввод. Как видите, теперь сделано 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

Примечание: Я только что увидел комментарий Andylamax о том, что новая версия «исправляет» это. Возможно, я не знаю, поскольку я не пробовал с этой новой версией. Но я хотел бы добавить, что это не что-то уникальное для Ктора, и, по крайней мере, в этом отношении это не ошибка (но, возможно, они передумали? Опять же, я не знаю). Фактически, именно мой опыт с C# заставил меня заподозрить, что происходит здесь, и найти ответ. WebRequest в C# ведет себя так же, вам нужно установить PreAuthenticate в true для немедленной отправки учетных данных. Смотрите здесь https://docs.microsoft.com/en-us/dotnet/api/system.net.webrequest.preauthenticate?view=netcore-3.1.

...