Рубрики
Без рубрики

Добавьте заголовок к запросу клиента SSE Джерси

Узнайте, как добавлять заголовки в запросы клиентов SSE в Джерси с помощью фильтров.

Автор оригинала: Fabio Silva.

1. Обзор

В этом руководстве мы увидим простой способ отправки заголовков в клиентских запросах событий, отправленных сервером (SSE), с помощью API клиента Jersey.

Мы также рассмотрим правильный способ отправки основных заголовков ключей/значений, заголовков аутентификации и ограниченных заголовков с использованием транспортного соединителя Джерси по умолчанию.

2. Прямо к делу

Вероятно, мы все сталкивались с этой ситуацией, пытаясь отправить заголовки с помощью SSES:

Мы используем Sse EventSource для получения проблем, но для создания Sse EventSource нам нужен экземпляр WebTarget , который не предоставляет нам способ добавления заголовков. Экземпляр Client тоже не поможет. Звучит знакомо?

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

Давайте посмотрим, что мы можем сделать с помощью ClientRequestFilter .

3. Зависимости

Чтобы начать наше путешествие, нам нужно |/джерси-клиент зависимость , а также Зависимость от размера Джерси в вашем Maven pom.xml файл:


    org.glassfish.jersey.core
    jersey-client
    2.29

    org.glassfish.jersey.media
    jersey-media-sse
    2.29

Обратите внимание, что Jersey поддерживает JAX-RS 2.1 начиная с 2.29, так что, похоже, мы сможем использовать функции из этого.

4. ClientRequestFilter

Во-первых, мы реализуем фильтр, который добавит заголовок к каждому запросу клиента:

public class AddHeaderOnRequestFilter implements ClientRequestFilter {

    public static final String FILTER_HEADER_VALUE = "filter-header-value";
    public static final String FILTER_HEADER_KEY = "x-filter-header";

    @Override
    public void filter(ClientRequestContext requestContext) throws IOException {
        requestContext.getHeaders().add(FILTER_HEADER_KEY, FILTER_HEADER_VALUE);
    }
}

После этого мы зарегистрируем его и потребим.

Для наших примеров мы будем использовать https://sse.example.org как воображаемая конечная точка, из которой мы хотим, чтобы наш клиент потреблял события. На самом деле мы изменили бы это на реальную конечную точку сервера событий , которую мы хотим, чтобы наш клиент использовал.

Client client = ClientBuilder.newBuilder()
  .register(AddHeaderOnRequestFilter.class)
  .build();

WebTarget webTarget = client.target("https://sse.example.org/");

SseEventSource sseEventSource = SseEventSource.target(webTarget).build();
sseEventSource.register((event) -> { /* Consume event here */ });
sseEventSource.open();
// do something here until ready to close
sseEventSource.close();

Теперь, что, если нам нужно отправить более сложные заголовки, такие как заголовки аутентификации, в нашу конечную точку SSE?

Давайте вместе перейдем к следующим разделам, чтобы узнать больше о заголовках в API клиента Jersey.

5. Заголовки в API клиента Джерси

Важно знать, что реализация transportconnector Джерси по умолчанию использует класс HttpURLConnection из JDK . Этот класс ограничивает использование некоторых заголовков. Чтобы избежать этого ограничения, мы можем установить системное свойство:

System.setProperty("sun.net.http.allowRestrictedHeaders", "true");

Мы можем найти список ограниченных заголовков в Jersey docs .

5.1. Простые общие заголовки

Самый простой способ определить заголовок-это вызов WebTarget#request для получения вызова .Конструктор , который предоставляет метод заголовка .

public Response simpleHeader(String headerKey, String headerValue) {
    Client client = ClientBuilder.newClient();
    WebTarget webTarget = client.target("https://sse.example.org/");
    Invocation.Builder invocationBuilder = webTarget.request();
    invocationBuilder.header(headerKey, headerValue);
    return invocationBuilder.get();
}

И, на самом деле, мы можем довольно хорошо сжать это для дополнительной удобочитаемости:

public Response simpleHeaderFluently(String headerKey, String headerValue) {
    Client client = ClientBuilder.newClient();

    return client.target("https://sse.example.org/")
      .request()
      .header(headerKey, headerValue)
      .get();
}

Отсюда мы будем использовать только беглую форму для образцов, так как это легче понять.

5.2. Базовая Аутентификация

На самом деле, клиентский API Джерси предоставляет класс HttpAuthenticationFeature , который позволяет нам легко отправлять заголовки аутентификации :

public Response basicAuthenticationAtClientLevel(String username, String password) {
    HttpAuthenticationFeature feature = HttpAuthenticationFeature.basic(username, password);
    Client client = ClientBuilder.newBuilder().register(feature).build();

    return client.target("https://sse.example.org/")
      .request()
      .get();
}

Поскольку мы зарегистрировали функцию при создании клиента, она будет применяться к каждому запросу. API обрабатывает кодировку имени пользователя и пароля, которые требуются в базовой спецификации.

Обратите внимание, кстати, что мы подразумеваем HTTPS в качестве режима для нашего соединения. Хотя это всегда ценная мера безопасности, она имеет основополагающее значение при использовании базовой аутентификации, поскольку в противном случае пароль отображается в виде открытого текста. Джерси также поддерживает более сложные конфигурации безопасности .

Теперь мы также можем указать кредитные карты по запросу:

public Response basicAuthenticationAtRequestLevel(String username, String password) {
    HttpAuthenticationFeature feature = HttpAuthenticationFeature.basicBuilder().build();
    Client client = ClientBuilder.newBuilder().register(feature).build();

    return client.target("https://sse.example.org/")
      .request()
      .property(HTTP_AUTHENTICATION_BASIC_USERNAME, username)
      .property(HTTP_AUTHENTICATION_BASIC_PASSWORD, password)
      .get();
}

5.3. Дайджест-Аутентификация

Джерси HttpAuthenticationFeature также поддерживает дайджест-аутентификацию:

public Response digestAuthenticationAtClientLevel(String username, String password) {
    HttpAuthenticationFeature feature = HttpAuthenticationFeature.digest(username, password);
    Client client = ClientBuilder.newBuilder().register(feature).build();

    return client.target("https://sse.example.org/")
      .request()
      .get();
}

И, точно так же, мы можем переопределить во время запроса:

public Response digestAuthenticationAtRequestLevel(String username, String password) {
    HttpAuthenticationFeature feature = HttpAuthenticationFeature.digest();
    Client client = ClientBuilder.newBuilder().register(feature).build();

    return client.target("http://sse.example.org/")
      .request()
      .property(HTTP_AUTHENTICATION_DIGEST_USERNAME, username)
      .property(HTTP_AUTHENTICATION_DIGEST_PASSWORD, password)
      .get();
}

5.4. Аутентификация токенов на предъявителя с помощью OAuth 2.0

OAuth 2.0 поддерживает понятие токенов на предъявителя в качестве еще одного механизма аутентификации.

Нам понадобится Jersey’s oauth2client dependency , чтобы предоставить нам Функцию поддержки клиентов OAuth2 , которая похожа на HttpAuthenticationFeature :


    org.glassfish.jersey.security
    oauth2-client
    2.29

Чтобы добавить токен на предъявителя, мы будем следовать той же схеме, что и раньше:

public Response bearerAuthenticationWithOAuth2AtClientLevel(String token) {
    Feature feature = OAuth2ClientSupport.feature(token);
    Client client = ClientBuilder.newBuilder().register(feature).build();

    return client.target("https://sse.examples.org/")
      .request()
      .get();
}

Или мы можем переопределить на уровне запроса, что особенно удобно, когда токен изменяется из-за поворота:

public Response bearerAuthenticationWithOAuth2AtRequestLevel(String token, String otherToken) {
    Feature feature = OAuth2ClientSupport.feature(token);
    Client client = ClientBuilder.newBuilder().register(feature).build();

    return client.target("https://sse.example.org/")
      .request()
      .property(OAuth2ClientSupport.OAUTH2_PROPERTY_ACCESS_TOKEN, otherToken)
      .get();
}

5.5. Аутентификация токенов на предъявителя с помощью OAuth 1.0

В-четвертых, если нам нужно интегрироваться с устаревшим кодом, использующим OAuth 1.0, нам понадобится Джерси oauth1-клиент зависимость :


    org.glassfish.jersey.security
    oauth1-client
    2.29

И аналогично OAuth 2.0, у нас есть OAuth1ClientSupport что мы можем использовать:

public Response bearerAuthenticationWithOAuth1AtClientLevel(String token, String consumerKey) {
    ConsumerCredentials consumerCredential = 
      new ConsumerCredentials(consumerKey, "my-consumer-secret");
    AccessToken accessToken = new AccessToken(token, "my-access-token-secret");

    Feature feature = OAuth1ClientSupport
      .builder(consumerCredential)
      .feature()
      .accessToken(accessToken)
      .build();

    Client client = ClientBuilder.newBuilder().register(feature).build();

    return client.target("https://sse.example.org/")
      .request()
      .get();
}

При этом уровень запроса снова включается с помощью OAuth1ClientSupport.OAUTH_PROPERTY_ACCESS_TOKEN свойство.

6. Заключение

Подводя итог, в этой статье мы рассмотрели, как добавлять заголовки к клиентским запросам SSE в Джерси с помощью фильтров. Мы также специально рассмотрели, как работать с заголовками аутентификации.

Код для этого примера доступен на GitHub .