Аутентификация по токену-носителю JWT с компонентом веб-сокета Apache Camel AHC-WS - PullRequest
0 голосов
/ 07 июня 2019

Контекст

В настоящее время мы настраиваем проект Springboot, используя Apache Camel в качестве инфраструктуры интеграции.

В конце нашего маршрута нам нужно отправлять сообщения с протоколом веб-сокетов WSS, где наше приложение действует как клиент и должно отправлять сообщения на удаленный сервер, который предоставляет конечную точку веб-сокета.

Но удаленный сервер сначала требует аутентификацию во время рукопожатия WSS. На самом деле для этого требуются определенные заголовки HTTP, включая заголовок Authorization: Bearer {jwt-bearer-token}. Токен требуется только во время рукопожатия. После того, как веб-розетка была открыта, больше не требуется дополнительная аутентификация.

1010 * подход * Поскольку это, похоже, единственный клиентский компонент веб-сокета для Apache Camel, мы намеревались использовать AHC-WS компонент , как показано в POM ниже: ... <dependency> <groupId>org.apache.camel</groupId> <artifactId>camel-spring-boot-starter</artifactId> <version>3.0.0-M2</version> </dependency> <dependency> <groupId>org.apache.camel</groupId> <artifactId>camel-ahc-ws</artifactId> <version>3.0.0-M2</version> </dependency> ... В нашем определении маршрута мы затем добавляем необходимые заголовки к сообщению (например, заголовок Authorization с токеном-носителем JWT). Кроме того, мы регистрируем токен, чтобы увидеть, действительно ли он добавлен в качестве заголовка сообщения (что это такое). <?xml version="1.0" encoding="UTF-8"?> <routes xmlns="http://camel.apache.org/schema/spring"> <route id="distributorRoute"> <from uri="seda:distributorEntryPoint"/> <setHeader headerName="Authorization"> <simple>Bearer {jwt-bearer-token}</simple> </setHeader> <log message="${header.Authorization}"/> <to uri="ahc-wss://{remote-server-wss-endpoint}> </route> </routes> Проблема

Проще говоря, сообщения не могут быть отправлены через WSS, и кажется, что соединение никогда не устанавливается.

Приложение выдает следующее исключение NPE:

2019-06-07 15:52:00.304  INFO 17164 --- [butorEntryPoint] o.a.camel.component.ahc.ws.WsEndpoint    : Reconnecting websocket: wss://{remote-server-wss-endpoint}

...

java.lang.NullPointerException: null
    at org.apache.camel.component.ahc.ws.WsProducer.sendMessage(WsProducer.java:76) ~[camel-ahc-ws-3.0.0-M2.jar:3.0.0-M2]
    at org.apache.camel.component.ahc.ws.WsProducer.process(WsProducer.java:51) ~[camel-ahc-ws-3.0.0-M2.jar:3.0.0-M2]
    at org.apache.camel.support.AsyncProcessorConverterHelper$ProcessorToAsyncProcessorBridge.process(AsyncProcessorConverterHelper.java:66) ~[camel-support-3.0.0-M2.jar:3.0.0-M2]
    at org.apache.camel.processor.SendProcessor.process(SendProcessor.java:130) ~[camel-core-3.0.0-M2.jar:3.0.0-M2]
    at org.apache.camel.processor.RedeliveryErrorHandler$RedeliveryState.run(RedeliveryErrorHandler.java:482) ~[camel-core-3.0.0-M2.jar:3.0.0-M2]
    at org.apache.camel.support.ReactiveHelper$Worker.schedule(ReactiveHelper.java:130) [camel-support-3.0.0-M2.jar:3.0.0-M2]
    at org.apache.camel.support.ReactiveHelper.scheduleMain(ReactiveHelper.java:43) [camel-support-3.0.0-M2.jar:3.0.0-M2]
    at org.apache.camel.processor.Pipeline.process(Pipeline.java:88) [camel-core-3.0.0-M2.jar:3.0.0-M2]
    at org.apache.camel.processor.CamelInternalProcessor.process(CamelInternalProcessor.java:221) [camel-core-3.0.0-M2.jar:3.0.0-M2]
    at org.apache.camel.component.seda.SedaConsumer.sendToConsumers(SedaConsumer.java:289) [camel-seda-3.0.0-M2.jar:3.0.0-M2]
    at org.apache.camel.component.seda.SedaConsumer.doRun(SedaConsumer.java:203) [camel-seda-3.0.0-M2.jar:3.0.0-M2]
    at org.apache.camel.component.seda.SedaConsumer.run(SedaConsumer.java:148) [camel-seda-3.0.0-M2.jar:3.0.0-M2]
    at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1135) [na:na]
    at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635) [na:na]
    at java.base/java.lang.Thread.run(Thread.java:844) [na:na]

Во время отладки мы обнаружили, что org.apache.camel.component.ahc.ws.WsProducer пытается отправить сообщение с объектом org.apache.camel.component.ahc.ws.WebSocket, в нашем случае null:

WsProducer

Однако компонент, кажется, подключен правильно, поскольку отправка на незащищенную конечную точку WS для целей тестирования работала как ожидалось. Затем он также создает журнал «Переподключение веб-сокета» только один раз.

Вопросы

Кто-нибудь уже сталкивался с подобной проблемой при аутентификации во время рукопожатия WS?

Мы уже изучили возможности конфигурации компонента AHC-WS, но ни одна из них, похоже, не подходит для этого случая.

Кроме того, нам нужно динамически генерировать и добавлять токен, поэтому жесткое кодирование не поможет.

1 Ответ

0 голосов
/ 19 июня 2019

Комментарий @paizo привел нас к идее переписать метод prepareGet() в пользовательской реализации DefaultAsyncHttpClient:

@Override
public BoundRequestBuilder prepareGet(String url) {
    // OAuth token
    String token = "Bearer my-jwt-token";

    // Headers for the handshake request
    HttpHeaders httpHeaders = new DefaultHttpHeaders();
    httpHeaders.set("Authorization", token);

    // Prepare the websocket upgrade handshake
    BoundRequestBuilder boundRequestBuilder = super.prepareGet(url);
    boundRequestBuilder.setHeaders(httpHeaders);    

    return boundRequestBuilder;
}

Теперь этот клиент должен использоваться компонентами AHC-WS. Фактически он используется классом WsEndpoint во время метода connect(), в котором getClient() должен возвращать пользовательский клиент сверху:

public void connect() throws Exception {
    String uri = getHttpUri().toASCIIString();

    log.debug("Connecting to {}", uri);
    websocket = getClient().prepareGet(uri).execute(
        new WebSocketUpgradeHandler.Builder()
            .addWebSocketListener(listener).build()).get();
}

К сожалению, компоненты AHC-WS недоступны в виде bean-компонентов Spring, но на пользовательский клиент можно указать из application.yml:

camel:
  component:
    ahc-ws:
      client: myCustomAsyncHttpClientImpl

Таким образом, рукопожатие обновления websocket может быть настроено.

...