org.glassfish.jersey.client.authentication.HttpAuthenticationFeature Maven / Gradle / Ivy
Show all versions of jaxrs-ri Show documentation
/*
* Copyright (c) 2013, 2020 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the
* Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
* version 2 with the GNU Classpath Exception, which is available at
* https://www.gnu.org/software/classpath/license.html.
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
*/
package org.glassfish.jersey.client.authentication;
import jakarta.ws.rs.core.Feature;
import jakarta.ws.rs.core.FeatureContext;
/**
* Features that provides Http Basic and Digest client authentication (based on RFC 2617).
*
* The feature can work in following modes:
*
* - BASIC: Basic preemptive authentication. In preemptive mode the authentication information
* is send always with each HTTP request. This mode is more usual than the following non-preemptive mode
* (if you require BASIC authentication you will probably use this preemptive mode). This mode must
* be combined with usage of SSL/TLS as the password is send only BASE64 encoded.
* - BASIC NON-PREEMPTIVE: Basic non-preemptive authentication. In non-preemptive mode the
* authentication information is added only when server refuses the request with {@code 401} status code and
* then the request is repeated with authentication information. This mode has negative impact on the performance.
* The advantage is that it does not send credentials when they are not needed. This mode must
* be combined with usage of SSL/TLS as the password is send only BASE64 encoded.
*
* Please note that when you use non-preemptive authentication, Jersey client will make 2 requests to a resource,
* which also means that all registered filters will be invoked twice.
*
* - DIGEST: Http digest authentication. Does not require usage of SSL/TLS.
* - UNIVERSAL: Combination of basic and digest authentication. The feature works in non-preemptive
* mode which means that it sends requests without authentication information. If {@code 401} status
* code is returned, the request is repeated and an appropriate authentication is used based on the
* authentication requested in the response (defined in {@code WWW-Authenticate} HTTP header. The feature
* remembers which authentication requests were successful for given URI and next time tries to preemptively
* authenticate against this URI with latest successful authentication method.
*
*
*
*
* To initialize the feature use static method of this feature.
*
*
* Example of building the feature in
* Basic authentication mode:
*
* HttpAuthenticationFeature feature = HttpAuthenticationFeature.basic("user", "superSecretPassword");
*
*
*
* Example of building the feature in basic non-preemptive mode:
*
* HttpAuthenticationFeature feature = HttpAuthenticationFeature.basicBuilder()
* .nonPreemptive().credentials("user", "superSecretPassword").build();
*
*
*
* Example of building the feature in universal mode:
*
* HttpAuthenticationFeature feature = HttpAuthenticationFeature.universal("user", "superSecretPassword");
*
*
*
* Example of building the feature in universal mode with different credentials for basic and digest:
*
* HttpAuthenticationFeature feature = HttpAuthenticationFeature.universalBuilder()
* .credentialsForBasic("user", "123456")
* .credentials("adminuser", "hello")
* .build();
*
*
* Example of building the feature in basic preemptive mode with no default credentials. Credentials will have
* to be supplied with each request using request properties (see below):
*
* HttpAuthenticationFeature feature = HttpAuthenticationFeature.basicBuilder().build();
*
*
*
* Once the feature is built it needs to be registered into the {@link jakarta.ws.rs.client.Client},
* {@link jakarta.ws.rs.client.WebTarget} or other client configurable object. Example:
*
* final Client client = ClientBuilder.newClient();
* client.register(feature);
*
*
*
* Then you invoke requests as usual and authentication will be handled by the feature.
* You can change the credentials for each request using properties
* {@link org.glassfish.jersey.client.authentication.HttpAuthenticationFeature#HTTP_AUTHENTICATION_USERNAME} and
* {@link org.glassfish.jersey.client.authentication.HttpAuthenticationFeature#HTTP_AUTHENTICATION_PASSWORD}. Example:
*
* final Response response = client.target("http://localhost:8080/rest/homer/contact").request()
* .property(HTTP_AUTHENTICATION_BASIC_USERNAME, "homer")
* .property(HTTP_AUTHENTICATION_BASIC_PASSWORD, "p1swd745").get();
*
*
* This class also contains property key definitions for overriding only specific basic or digest credentials:
*
* -
* {@link org.glassfish.jersey.client.authentication.HttpAuthenticationFeature#HTTP_AUTHENTICATION_BASIC_USERNAME} and
* {@link org.glassfish.jersey.client.authentication.HttpAuthenticationFeature#HTTP_AUTHENTICATION_BASIC_PASSWORD}
*
* -
* {@link org.glassfish.jersey.client.authentication.HttpAuthenticationFeature#HTTP_AUTHENTICATION_DIGEST_USERNAME} and
* {@link org.glassfish.jersey.client.authentication.HttpAuthenticationFeature#HTTP_AUTHENTICATION_DIGEST_PASSWORD}.
*
*
*
*
* @author Miroslav Fuksa
*
* @since 2.5
*/
public class HttpAuthenticationFeature implements Feature {
/**
* Feature authentication mode.
*/
static enum Mode {
/**
* Basic preemptive.
**/
BASIC_PREEMPTIVE,
/**
* Basic non preemptive
*/
BASIC_NON_PREEMPTIVE,
/**
* Digest.
*/
DIGEST,
/**
* Universal.
*/
UNIVERSAL
}
/**
* Builder that creates instances of {@link HttpAuthenticationFeature}.
*/
public static interface Builder {
/**
* Set credentials.
*
* @param username Username.
* @param password Password as byte array.
* @return This builder.
*/
public Builder credentials(String username, byte[] password);
/**
* Set credentials.
*
* @param username Username.
* @param password Password as {@link String}.
* @return This builder.
*/
public Builder credentials(String username, String password);
/**
* Build the feature.
*
* @return Http authentication feature configured from this builder.
*/
public HttpAuthenticationFeature build();
}
/**
* Extension of {@link org.glassfish.jersey.client.authentication.HttpAuthenticationFeature.Builder}
* that builds the http authentication feature configured for basic authentication.
*/
public static interface BasicBuilder extends Builder {
/**
* Configure the builder to create features in non-preemptive basic authentication mode.
*
* @return This builder.
*/
public BasicBuilder nonPreemptive();
}
/**
* Extension of {@link org.glassfish.jersey.client.authentication.HttpAuthenticationFeature.Builder}
* that builds the http authentication feature configured in universal mode that supports
* basic and digest authentication.
*/
public static interface UniversalBuilder extends Builder {
/**
* Set credentials that will be used for basic authentication only.
*
* @param username Username.
* @param password Password as {@link String}.
* @return This builder.
*/
public UniversalBuilder credentialsForBasic(String username, String password);
/**
* Set credentials that will be used for basic authentication only.
*
* @param username Username.
* @param password Password as {@code byte array}.
* @return This builder.
*/
public UniversalBuilder credentialsForBasic(String username, byte[] password);
/**
* Set credentials that will be used for digest authentication only.
*
* @param username Username.
* @param password Password as {@link String}.
* @return This builder.
*/
public UniversalBuilder credentialsForDigest(String username, String password);
/**
* Set credentials that will be used for digest authentication only.
*
* @param username Username.
* @param password Password as {@code byte array}.
* @return This builder.
*/
public UniversalBuilder credentialsForDigest(String username, byte[] password);
}
/**
* Implementation of all authentication builders.
*/
static class BuilderImpl implements UniversalBuilder, BasicBuilder {
private String usernameBasic;
private byte[] passwordBasic;
private String usernameDigest;
private byte[] passwordDigest;
private Mode mode;
/**
* Create a new builder.
*
* @param mode Mode in which the final authentication feature should work.
*/
public BuilderImpl(Mode mode) {
this.mode = mode;
}
@Override
public Builder credentials(String username, String password) {
return credentials(username, password == null ? null : password.getBytes(HttpAuthenticationFilter.CHARACTER_SET));
}
@Override
public Builder credentials(String username, byte[] password) {
credentialsForBasic(username, password);
credentialsForDigest(username, password);
return this;
}
@Override
public UniversalBuilder credentialsForBasic(String username, String password) {
return credentialsForBasic(username,
password == null ? null : password.getBytes(HttpAuthenticationFilter.CHARACTER_SET));
}
@Override
public UniversalBuilder credentialsForBasic(String username, byte[] password) {
this.usernameBasic = username;
this.passwordBasic = password;
return this;
}
@Override
public UniversalBuilder credentialsForDigest(String username, String password) {
return credentialsForDigest(username,
password == null ? null : password.getBytes(HttpAuthenticationFilter.CHARACTER_SET));
}
@Override
public UniversalBuilder credentialsForDigest(String username, byte[] password) {
this.usernameDigest = username;
this.passwordDigest = password;
return this;
}
@Override
public HttpAuthenticationFeature build() {
return new HttpAuthenticationFeature(mode,
usernameBasic == null ? null
: new HttpAuthenticationFilter.Credentials(usernameBasic, passwordBasic),
usernameDigest == null ? null
: new HttpAuthenticationFilter.Credentials(usernameDigest, passwordDigest));
}
@Override
public BasicBuilder nonPreemptive() {
if (mode == Mode.BASIC_PREEMPTIVE) {
this.mode = Mode.BASIC_NON_PREEMPTIVE;
}
return this;
}
}
/**
* Key of the property that can be set into the {@link jakarta.ws.rs.client.ClientRequestContext client request}
* using {@link jakarta.ws.rs.client.ClientRequestContext#setProperty(String, Object)} in order to override
* the username for http authentication feature for the request.
*
* Example:
*
* Response response = client.target("http://localhost:8080/rest/joe/orders").request()
* .property(HTTP_AUTHENTICATION_USERNAME, "joe")
* .property(HTTP_AUTHENTICATION_PASSWORD, "p1swd745").get();
*
*
* The property must be always combined with configuration of {@link #HTTP_AUTHENTICATION_PASSWORD} property
* (as shown in the example). This property pair overrides all password settings of the authentication
* feature for the current request.
*
* The default value must be instance of {@link String}.
*
*
* The name of the configuration property is {@value}.
*
*/
public static final String HTTP_AUTHENTICATION_USERNAME = "jersey.config.client.http.auth.username";
/**
* Key of the property that can be set into the {@link jakarta.ws.rs.client.ClientRequestContext client request}
* using {@link jakarta.ws.rs.client.ClientRequestContext#setProperty(String, Object)} in order to override
* the password for http authentication feature for the request.
*
* Example:
*
* Response response = client.target("http://localhost:8080/rest/joe/orders").request()
* .property(HTTP_AUTHENTICATION_USERNAME, "joe")
* .property(HTTP_AUTHENTICATION_PASSWORD, "p1swd745").get();
*
*
* The property must be always combined with configuration of {@link #HTTP_AUTHENTICATION_USERNAME} property
* (as shown in the example). This property pair overrides all password settings of the authentication
* feature for the current request.
*
* The value must be instance of {@link String} or {@code byte} array ({@code byte[]}).
*
*
* The name of the configuration property is {@value}.
*
*/
public static final String HTTP_AUTHENTICATION_PASSWORD = "jersey.config.client.http.auth.password";
/**
* Key of the property that can be set into the {@link jakarta.ws.rs.client.ClientRequestContext client request}
* using {@link jakarta.ws.rs.client.ClientRequestContext#setProperty(String, Object)} in order to override
* the username for http basic authentication feature for the request.
*
* Example:
*
* Response response = client.target("http://localhost:8080/rest/joe/orders").request()
* .property(HTTP_AUTHENTICATION_BASIC_USERNAME, "joe")
* .property(HTTP_AUTHENTICATION_BASIC_PASSWORD, "p1swd745").get();
*
*
* The property must be always combined with configuration of {@link #HTTP_AUTHENTICATION_PASSWORD} property
* (as shown in the example). The property pair influence only credentials used during basic authentication.
*
*
* The value must be instance of {@link String}.
*
*
* The name of the configuration property is {@value}.
*
*/
public static final String HTTP_AUTHENTICATION_BASIC_USERNAME = "jersey.config.client.http.auth.basic.username";
/**
* Key of the property that can be set into the {@link jakarta.ws.rs.client.ClientRequestContext client request}
* using {@link jakarta.ws.rs.client.ClientRequestContext#setProperty(String, Object)} in order to override
* the password for http basic authentication feature for the request.
*
* Example:
*
* Response response = client.target("http://localhost:8080/rest/joe/orders").request()
* .property(HTTP_AUTHENTICATION_BASIC_USERNAME, "joe")
* .property(HTTP_AUTHENTICATION_BASIC_PASSWORD, "p1swd745").get();
*
*
* The property must be always combined with configuration of {@link #HTTP_AUTHENTICATION_USERNAME} property
* (as shown in the example). The property pair influence only credentials used during basic authentication.
*
* The value must be instance of {@link String} or {@code byte} array ({@code byte[]}).
*
*
* The name of the configuration property is {@value}.
*
*/
public static final String HTTP_AUTHENTICATION_BASIC_PASSWORD = "jersey.config.client.http.auth.basic.password";
/**
* Key of the property that can be set into the {@link jakarta.ws.rs.client.ClientRequestContext client request}
* using {@link jakarta.ws.rs.client.ClientRequestContext#setProperty(String, Object)} in order to override
* the username for http digest authentication feature for the request.
*
* Example:
*
* Response response = client.target("http://localhost:8080/rest/joe/orders").request()
* .property(HTTP_AUTHENTICATION_DIGEST_USERNAME, "joe")
* .property(HTTP_AUTHENTICATION_DIGEST_PASSWORD, "p1swd745").get();
*
*
* The property must be always combined with configuration of {@link #HTTP_AUTHENTICATION_PASSWORD} property
* (as shown in the example). The property pair influence only credentials used during digest authentication.
*
* The value must be instance of {@link String}.
*
*
* The name of the configuration property is {@value}.
*
*/
public static final String HTTP_AUTHENTICATION_DIGEST_USERNAME = "jersey.config.client.http.auth.digest.username";
/**
* Key of the property that can be set into the {@link jakarta.ws.rs.client.ClientRequestContext client request}
* using {@link jakarta.ws.rs.client.ClientRequestContext#setProperty(String, Object)} in order to override
* the password for http digest authentication feature for the request.
*
* Example:
*
* Response response = client.target("http://localhost:8080/rest/joe/orders").request()
* .property(HTTP_AUTHENTICATION_DIGEST_USERNAME, "joe")
* .property(HTTP_AUTHENTICATION_DIGEST_PASSWORD, "p1swd745").get();
*
*
* The property must be always combined with configuration of {@link #HTTP_AUTHENTICATION_PASSWORD} property
* (as shown in the example). The property pair influence only credentials used during digest authentication.
*
* The value must be instance of {@link String} or {@code byte} array ({@code byte[]}).
*
*
* The name of the configuration property is {@value}.
*
*/
public static final String HTTP_AUTHENTICATION_DIGEST_PASSWORD = "jersey.config.client.http.auth.digest.password";
/**
* Create the builder of the http authentication feature working in basic authentication mode. The builder
* can build preemptive and non-preemptive basic authentication features.
*
* @return Basic http authentication builder.
*/
public static BasicBuilder basicBuilder() {
return new BuilderImpl(Mode.BASIC_PREEMPTIVE);
}
/**
* Create the http authentication feature in basic preemptive authentication mode initialized with credentials.
*
* @param username Username.
* @param password Password as {@code byte array}.
* @return Http authentication feature configured in basic mode.
*/
public static HttpAuthenticationFeature basic(String username, byte[] password) {
return build(Mode.BASIC_PREEMPTIVE, username, password);
}
/**
* Create the http authentication feature in basic preemptive authentication mode initialized with credentials.
*
* @param username Username.
* @param password Password as {@link String}.
* @return Http authentication feature configured in basic mode.
*/
public static HttpAuthenticationFeature basic(String username, String password) {
return build(Mode.BASIC_PREEMPTIVE, username, password);
}
/**
* Create the http authentication feature in digest authentication mode initialized without default
* credentials. Credentials will have to be supplied using request properties for each request.
*
* @return Http authentication feature configured in digest mode.
*/
public static HttpAuthenticationFeature digest() {
return build(Mode.DIGEST);
}
/**
* Create the http authentication feature in digest authentication mode initialized with credentials.
*
* @param username Username.
* @param password Password as {@code byte array}.
* @return Http authentication feature configured in digest mode.
*/
public static HttpAuthenticationFeature digest(String username, byte[] password) {
return build(Mode.DIGEST, username, password);
}
/**
* Create the http authentication feature in digest authentication mode initialized with credentials.
*
* @param username Username.
* @param password Password as {@link String}.
* @return Http authentication feature configured in digest mode.
*/
public static HttpAuthenticationFeature digest(String username, String password) {
return build(Mode.DIGEST, username, password);
}
/**
* Create the builder that builds http authentication feature in combined mode supporting both,
* basic and digest authentication.
*
* @return Universal builder.
*/
public static UniversalBuilder universalBuilder() {
return new BuilderImpl(Mode.UNIVERSAL);
}
/**
* Create the http authentication feature in combined mode supporting both,
* basic and digest authentication.
*
* @param username Username.
* @param password Password as {@code byte array}.
* @return Http authentication feature configured in digest mode.
*/
public static HttpAuthenticationFeature universal(String username, byte[] password) {
return build(Mode.UNIVERSAL, username, password);
}
/**
* Create the http authentication feature in combined mode supporting both,
* basic and digest authentication.
*
* @param username Username.
* @param password Password as {@link String}.
* @return Http authentication feature configured in digest mode.
*/
public static HttpAuthenticationFeature universal(String username, String password) {
return build(Mode.UNIVERSAL, username, password);
}
private static HttpAuthenticationFeature build(Mode mode) {
return new BuilderImpl(mode).build();
}
private static HttpAuthenticationFeature build(Mode mode, String username, byte[] password) {
return new BuilderImpl(mode).credentials(username, password).build();
}
private static HttpAuthenticationFeature build(Mode mode, String username, String password) {
return new BuilderImpl(mode).credentials(username, password).build();
}
private final Mode mode;
private final HttpAuthenticationFilter.Credentials basicCredentials;
private final HttpAuthenticationFilter.Credentials digestCredentials;
private HttpAuthenticationFeature(Mode mode, HttpAuthenticationFilter.Credentials basicCredentials,
HttpAuthenticationFilter.Credentials digestCredentials) {
this.mode = mode;
this.basicCredentials = basicCredentials;
this.digestCredentials = digestCredentials;
}
@Override
public boolean configure(FeatureContext context) {
context.register(new HttpAuthenticationFilter(mode, basicCredentials, digestCredentials, context.getConfiguration()));
return true;
}
}