diff --git a/spring-web/src/main/java/org/springframework/http/HttpHeaders.java b/spring-web/src/main/java/org/springframework/http/HttpHeaders.java index 7af0991d26..d344978b6b 100644 --- a/spring-web/src/main/java/org/springframework/http/HttpHeaders.java +++ b/spring-web/src/main/java/org/springframework/http/HttpHeaders.java @@ -41,8 +41,6 @@ import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Set; -import java.util.function.Consumer; -import java.util.function.Supplier; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; @@ -710,6 +708,48 @@ public class HttpHeaders implements MultiValueMap, Serializable } } + /** + * Set the value of the {@linkplain #AUTHORIZATION Authorization} header to + * Basic Authentication based on the given username and password. + * @param username the username + * @param password the password + * @since 5.1 + * @see RFC 7617 + */ + public void setBasicAuth(String username, String password) { + setBasicAuth(username, password, null); + } + + /** + * Set the value of the {@linkplain #AUTHORIZATION Authorization} header to + * Basic Authentication based on the given username and password. + * @param username the username + * @param password the password + * @param charset the charset to use to convert the credentials into an octet sequence. Defaults + * to {@linkplain StandardCharsets#ISO_8859_1 ISO-8859-1} + * @since 5.1 + * @see RFC 7617 + */ + public void setBasicAuth(String username, String password, @Nullable Charset charset) { + Assert.notNull(username, "Username must not be null"); + Assert.notNull(password, "Password must not be null"); + if (charset == null) { + charset = StandardCharsets.ISO_8859_1; + } + + CharsetEncoder encoder = charset.newEncoder(); + if (!encoder.canEncode(username) || !encoder.canEncode(password)) { + throw new IllegalArgumentException( + "Username or password contains characters that cannot be encoded to " + + charset.displayName()); + } + + String credentialsString = username + ":" + password; + byte[] encodedBytes = Base64.getEncoder().encode(credentialsString.getBytes(charset)); + String encodedCredentials = new String(encodedBytes, charset); + set(HttpHeaders.AUTHORIZATION, "Basic " + encodedCredentials); + } + /** * Set a configured {@link CacheControl} instance as the * new value of the {@code Cache-Control} header. @@ -1569,80 +1609,4 @@ public class HttpHeaders implements MultiValueMap, Serializable return (headers.readOnly ? headers : new HttpHeaders(headers, true)); } - /** - * Return a {@code HttpHeaders} consumer that adds Basic Authentication. - * More specifically: a consumer that adds an {@linkplain #AUTHORIZATION - * Authorization} header based on the given username and password. - * Meant to be used in combination with - * {@link org.springframework.web.reactive.function.client.WebClient.RequestHeadersSpec#headers(java.util.function.Consumer)}. - *

Note that Basic Authentication only supports characters in the - * {@linkplain StandardCharsets#ISO_8859_1 ISO-8859-1} character set. - * @param username the username - * @param password the password - * @return a consumer that adds a Basic Authentication header - * @since 5.1 - */ - public static Consumer basicAuthenticationConsumer(String username, String password) { - return basicAuthenticationConsumer(() -> username, () -> password); - - } - - /** - * Return a {@code HttpHeaders} consumer that adds Basic Authentication. - * More specifically: a consumer that adds an {@linkplain #AUTHORIZATION - * Authorization} header based on the given username and password - * suppliers. Meant to be used in combination with - * {@link org.springframework.web.reactive.function.client.WebClient.RequestHeadersSpec#headers(java.util.function.Consumer)}. - *

Note that Basic Authentication only supports characters in the - * {@linkplain StandardCharsets#ISO_8859_1 ISO-8859-1} character set. - * @param usernameSupplier supplier for the username - * @param passwordSupplier supplier for the password - * @return a consumer that adds a Basic Authentication header - * @since 5.1 - */ - public static Consumer basicAuthenticationConsumer( - Supplier usernameSupplier, Supplier passwordSupplier) { - - Assert.notNull(usernameSupplier, "Username Supplier must not be null"); - Assert.notNull(passwordSupplier, "Password Supplier must not be null"); - return new BasicAuthenticationConsumer(usernameSupplier, passwordSupplier); - } - - - private static class BasicAuthenticationConsumer implements Consumer { - - private final Supplier usernameSupplier; - - private final Supplier passwordSupplier; - - public BasicAuthenticationConsumer(Supplier usernameSupplier, Supplier passwordSupplier) { - this.usernameSupplier = usernameSupplier; - this.passwordSupplier = passwordSupplier; - } - - @Override - public void accept(HttpHeaders httpHeaders) { - String username = this.usernameSupplier.get(); - String password = this.passwordSupplier.get(); - Assert.state(username != null, "Supplied username is null"); - Assert.state(password != null, "Supplied password is null"); - checkIllegalCharacters(username, password); - - String credentialsString = username + ":" + password; - byte[] credentialBytes = credentialsString.getBytes(StandardCharsets.ISO_8859_1); - byte[] encodedBytes = Base64.getEncoder().encode(credentialBytes); - String encodedCredentials = new String(encodedBytes, StandardCharsets.ISO_8859_1); - httpHeaders.set(HttpHeaders.AUTHORIZATION, "Basic " + encodedCredentials); - } - - private static void checkIllegalCharacters(String username, String password) { - // Basic authentication only supports ISO 8859-1 - CharsetEncoder encoder = StandardCharsets.ISO_8859_1.newEncoder(); - if (!encoder.canEncode(username) || !encoder.canEncode(password)) { - throw new IllegalArgumentException( - "Username or password contains characters that cannot be encoded to ISO-8859-1"); - } - } - } - } diff --git a/spring-web/src/test/java/org/springframework/http/HttpHeadersTests.java b/spring-web/src/test/java/org/springframework/http/HttpHeadersTests.java index a3924ffc14..9b4876f7ac 100644 --- a/spring-web/src/test/java/org/springframework/http/HttpHeadersTests.java +++ b/spring-web/src/test/java/org/springframework/http/HttpHeadersTests.java @@ -34,7 +34,6 @@ import java.util.GregorianCalendar; import java.util.List; import java.util.Locale; import java.util.TimeZone; -import java.util.function.Consumer; import org.hamcrest.Matchers; import org.junit.Test; @@ -532,22 +531,22 @@ public class HttpHeadersTests { } @Test - public void basicAuthenticationConsumer() throws Exception { - + public void basicAuth() { String username = "foo"; String password = "bar"; - - Consumer consumer = - HttpHeaders.basicAuthenticationConsumer(username, password); - - HttpHeaders headers = new HttpHeaders(); - assertFalse(headers.containsKey(HttpHeaders.AUTHORIZATION)); - consumer.accept(headers); + headers.setBasicAuth(username, password); String authorization = headers.getFirst(HttpHeaders.AUTHORIZATION); assertNotNull(authorization); assertTrue(authorization.startsWith("Basic ")); byte[] result = Base64.getDecoder().decode(authorization.substring(6).getBytes(StandardCharsets.ISO_8859_1)); assertEquals("foo:bar", new String(result, StandardCharsets.ISO_8859_1)); - } + + @Test(expected = IllegalArgumentException.class) + public void basicAuthIllegalChar() { + String username = "foo"; + String password = "\u03BB"; + headers.setBasicAuth(username, password); + } + } diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/ExchangeFilterFunctions.java b/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/ExchangeFilterFunctions.java index 4bb4fb5714..4e580e7843 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/ExchangeFilterFunctions.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/ExchangeFilterFunctions.java @@ -44,7 +44,9 @@ public abstract class ExchangeFilterFunctions { /** * Name of the {@linkplain ClientRequest#attributes() request attribute} that * contains the {@link Credentials} used by {@link #basicAuthentication()}. + * @deprecated in favor of {@link HttpHeaders#setBasicAuth(String, String)} */ + @Deprecated public static final String BASIC_AUTHENTICATION_CREDENTIALS_ATTRIBUTE = ExchangeFilterFunctions.class.getName() + ".basicAuthenticationCredentials"; @@ -59,7 +61,9 @@ public abstract class ExchangeFilterFunctions { * @return the filter for basic authentication * @throws IllegalArgumentException if either {@code user} or * {@code password} contain characters that cannot be encoded to ISO-8859-1. + * @deprecated in favor of {@link HttpHeaders#setBasicAuth(String, String)} */ + @Deprecated public static ExchangeFilterFunction basicAuthentication(String user, String password) { Assert.notNull(user, "'user' must not be null"); Assert.notNull(password, "'password' must not be null"); @@ -75,7 +79,9 @@ public abstract class ExchangeFilterFunctions { * @return the filter for basic authentication * @see #BASIC_AUTHENTICATION_CREDENTIALS_ATTRIBUTE * @see Credentials#basicAuthenticationCredentials(String, String) + * @deprecated as of Spring 5.1, with no direct replacement */ + @Deprecated public static ExchangeFilterFunction basicAuthentication() { return basicAuthenticationInternal(request -> request.attribute(BASIC_AUTHENTICATION_CREDENTIALS_ATTRIBUTE) @@ -134,7 +140,9 @@ public abstract class ExchangeFilterFunctions { * Stores user and password for HTTP basic authentication. * @see #basicAuthentication() * @see #basicAuthenticationCredentials(String, String) + * @deprecated as of Spring 5.1, with no direct replacement */ + @Deprecated public static final class Credentials { private final String username; diff --git a/spring-webflux/src/test/java/org/springframework/web/reactive/function/client/ExchangeFilterFunctionsTests.java b/spring-webflux/src/test/java/org/springframework/web/reactive/function/client/ExchangeFilterFunctionsTests.java index a290aec719..b1752950b7 100644 --- a/spring-webflux/src/test/java/org/springframework/web/reactive/function/client/ExchangeFilterFunctionsTests.java +++ b/spring-webflux/src/test/java/org/springframework/web/reactive/function/client/ExchangeFilterFunctionsTests.java @@ -27,12 +27,13 @@ import org.springframework.http.HttpStatus; import static org.junit.Assert.*; import static org.mockito.Mockito.*; -import static org.springframework.http.HttpMethod.*; -import static org.springframework.web.reactive.function.client.ExchangeFilterFunctions.Credentials.*; +import static org.springframework.http.HttpMethod.GET; +import static org.springframework.web.reactive.function.client.ExchangeFilterFunctions.Credentials.basicAuthenticationCredentials; /** * @author Arjen Poutsma */ +@SuppressWarnings("deprecation") public class ExchangeFilterFunctionsTests { @Test