Update deprecated basic auth client filters.

1. Update ExchangeFilterFunctions to delegate internally to
HttpHeaders.setBasicAuth(user, password).

2. Remove deprecation from
ExchangeFilterFunctions.basicAuthentication(String user, String password)
It is still useful as a filter to insert the header.

3. Update deprecation notes.

Issue: SPR-17099
This commit is contained in:
Rossen Stoyanchev 2018-08-07 10:10:27 +03:00
parent 6b82a6c38c
commit 4a18488f30
3 changed files with 62 additions and 82 deletions

View File

@ -711,8 +711,12 @@ public class HttpHeaders implements MultiValueMap<String, String>, Serializable
/**
* Set the value of the {@linkplain #AUTHORIZATION Authorization} header to
* Basic Authentication based on the given username and password.
* <p>Note that Basic Authentication only supports characters in the
* {@link StandardCharsets#ISO_8859_1 ISO-8859-1} character set.
* @param username the username
* @param password the password
* @throws IllegalArgumentException if either {@code user} or
* {@code password} contain characters that cannot be encoded to ISO-8859-1.
* @since 5.1
* @see <a href="https://tools.ietf.org/html/rfc7617">RFC 7617</a>
*/
@ -725,8 +729,10 @@ public class HttpHeaders implements MultiValueMap<String, String>, Serializable
* 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}
* @param charset the charset to use to convert the credentials into an octet
* sequence. Defaults to {@linkplain StandardCharsets#ISO_8859_1 ISO-8859-1}
* @throws IllegalArgumentException if either {@code user} or
* {@code password} contain characters that cannot be encoded to ISO-8859-1.
* @since 5.1
* @see <a href="https://tools.ietf.org/html/rfc7617">RFC 7617</a>
*/

View File

@ -16,11 +16,8 @@
package org.springframework.web.reactive.function.client;
import java.nio.charset.CharsetEncoder;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.nio.charset.Charset;
import java.util.Map;
import java.util.Optional;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
@ -46,9 +43,9 @@ import org.springframework.web.reactive.function.BodyExtractors;
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)}
* Name of the request attribute with {@link Credentials} for {@link #basicAuthentication()}.
* @deprecated as of Spring 5.1 in favor of using
* {@link HttpHeaders#setBasicAuth(String, String)} while building the request.
*/
@Deprecated
public static final String BASIC_AUTHENTICATION_CREDENTIALS_ATTRIBUTE =
@ -72,73 +69,6 @@ public abstract class ExchangeFilterFunctions {
});
}
/**
* Return a filter for HTTP Basic Authentication that adds an authorization
* header, based on the given user and password.
* <p>Note that Basic Authentication only supports characters in the
* {@link StandardCharsets#ISO_8859_1 ISO-8859-1} character set.
* @param user the user
* @param password the password
* @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");
checkIllegalCharacters(user, password);
return basicAuthenticationInternal(request -> Optional.of(new Credentials(user, password)));
}
/**
* Variant of {@link #basicAuthentication(String, String)} that looks up
* the {@link Credentials Credentials} provided in a
* {@linkplain ClientRequest#attributes() request attribute}, or if the
* attribute is not found, the authorization header is not added.
* @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)
.map(credentials -> (Credentials) credentials));
}
@Deprecated
private static ExchangeFilterFunction basicAuthenticationInternal(
Function<ClientRequest, Optional<Credentials>> credentialsFunction) {
return ExchangeFilterFunction.ofRequestProcessor(request ->
credentialsFunction.apply(request)
.map(credentials -> Mono.just(insertAuthorizationHeader(request, credentials)))
.orElseGet(() -> Mono.just(request)));
}
private static void checkIllegalCharacters(String username, String password) {
// Basic authentication only supports ISO 8859-1, see
// https://stackoverflow.com/questions/702629/utf-8-characters-mangled-in-http-basic-auth-username#703341
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");
}
}
private static ClientRequest insertAuthorizationHeader(ClientRequest request, Credentials credentials) {
return ClientRequest.from(request).headers(headers -> {
String credentialsString = credentials.username + ":" + credentials.password;
byte[] credentialBytes = credentialsString.getBytes(StandardCharsets.ISO_8859_1);
byte[] encodedBytes = Base64.getEncoder().encode(credentialBytes);
String encodedCredentials = new String(encodedBytes, StandardCharsets.ISO_8859_1);
headers.set(HttpHeaders.AUTHORIZATION, "Basic " + encodedCredentials);
}).build();
}
/**
* Return a filter that generates an error signal when the given
* {@link HttpStatus} predicate matches.
@ -157,12 +87,55 @@ public abstract class ExchangeFilterFunctions {
Mono.error(exceptionFunction.apply(response)) : Mono.just(response)));
}
/**
* Return a filter that applies HTTP Basic Authentication to the request
* headers via {@link HttpHeaders#setBasicAuth(String, String)}.
* @param user the user
* @param password the password
* @return the filter to add authentication headers with
* @see HttpHeaders#setBasicAuth(String, String)
* @see HttpHeaders#setBasicAuth(String, String, Charset)
*/
public static ExchangeFilterFunction basicAuthentication(String user, String password) {
return (request, next) ->
next.exchange(ClientRequest.from(request)
.headers(headers -> headers.setBasicAuth(user, password))
.build());
}
/**
* Variant of {@link #basicAuthentication(String, String)} that looks up
* the {@link Credentials Credentials} in a
* {@link #BASIC_AUTHENTICATION_CREDENTIALS_ATTRIBUTE request attribute}.
* @return the filter to use
* @see Credentials
* @deprecated as of Spring 5.1 in favor of using
* {@link HttpHeaders#setBasicAuth(String, String)} while building the request.
*/
@Deprecated
public static ExchangeFilterFunction basicAuthentication() {
return (request, next) -> {
Credentials cred = (Credentials) request
.attribute(BASIC_AUTHENTICATION_CREDENTIALS_ATTRIBUTE).orElse(null);
if (cred != null) {
return next.exchange(ClientRequest.from(request)
.headers(headers -> headers.setBasicAuth(cred.username, cred.password))
.build());
}
else {
return next.exchange(request);
}
};
}
/**
* 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 as of Spring 5.1 in favor of using
* {@link HttpHeaders#setBasicAuth(String, String)} while building the request.
*/
@Deprecated
public static final class Credentials {
@ -196,7 +169,6 @@ public abstract class ExchangeFilterFunctions {
*/
public static Consumer<Map<String, Object>> basicAuthenticationCredentials(String user, String password) {
Credentials credentials = new Credentials(user, password);
checkIllegalCharacters(user, password);
return (map -> map.put(BASIC_AUTHENTICATION_CREDENTIALS_ATTRIBUTE, credentials));
}

View File

@ -37,6 +37,7 @@ import static org.junit.Assert.*;
import static org.mockito.Mockito.*;
/**
* Unit tests for {@link ExchangeFilterFunctions}.
* @author Arjen Poutsma
*/
public class ExchangeFilterFunctionsTests {
@ -94,7 +95,6 @@ public class ExchangeFilterFunctionsTests {
}
@Test
@SuppressWarnings("deprecation")
public void basicAuthenticationUsernamePassword() {
ClientRequest request = ClientRequest.create(HttpMethod.GET, DEFAULT_URL).build();
ClientResponse response = mock(ClientResponse.class);
@ -113,8 +113,10 @@ public class ExchangeFilterFunctionsTests {
@Test(expected = IllegalArgumentException.class)
public void basicAuthenticationInvalidCharacters() {
ClientRequest request = ClientRequest.create(HttpMethod.GET, DEFAULT_URL).build();
ExchangeFunction exchange = r -> Mono.just(mock(ClientResponse.class));
ExchangeFilterFunctions.basicAuthentication("foo", "\ud83d\udca9");
ExchangeFilterFunctions.basicAuthentication("foo", "\ud83d\udca9").filter(request, exchange);
}
@Test