Merge branch '7.0.x'

This commit is contained in:
Sam Brannen
2026-05-20 17:49:42 +02:00
2 changed files with 44 additions and 2 deletions
@@ -17,9 +17,11 @@
package org.springframework.web.reactive.function.client;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.function.Function;
import java.util.function.Predicate;
import org.jspecify.annotations.Nullable;
import reactor.core.publisher.Mono;
import org.springframework.core.io.buffer.DataBufferUtils;
@@ -34,6 +36,7 @@ import org.springframework.util.Assert;
* @author Rob Winch
* @author Arjen Poutsma
* @author Sam Brannen
* @author Kai Zander
* @since 5.0
*/
public abstract class ExchangeFilterFunctions {
@@ -76,14 +79,35 @@ public abstract class ExchangeFilterFunctions {
* Return a filter that applies HTTP Basic Authentication to the request
* headers via {@link HttpHeaders#setBasicAuth(String)} and
* {@link HttpHeaders#encodeBasicAuth(String, String, Charset)}.
* <p>{@linkplain StandardCharsets#ISO_8859_1 ISO-8859-1} is used to convert
* the credentials into an octet sequence.
* @param username the username
* @param password the password
* @return the filter to add authentication headers with
* @see #basicAuthentication(String, String, Charset)
* @see HttpHeaders#encodeBasicAuth(String, String, Charset)
* @see HttpHeaders#setBasicAuth(String)
*/
public static ExchangeFilterFunction basicAuthentication(String username, String password) {
String encodedCredentials = HttpHeaders.encodeBasicAuth(username, password, null);
return basicAuthentication(username, password, null);
}
/**
* Return a filter that applies HTTP Basic Authentication to the request
* headers via {@link HttpHeaders#setBasicAuth(String)} and
* {@link HttpHeaders#encodeBasicAuth(String, String, Charset)}.
* @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}.
* @return the filter to add authentication headers with
* @since 7.0.8
* @see #basicAuthentication(String, String)
* @see HttpHeaders#encodeBasicAuth(String, String, Charset)
* @see HttpHeaders#setBasicAuth(String)
*/
public static ExchangeFilterFunction basicAuthentication(String username, String password, @Nullable Charset charset) {
String encodedCredentials = HttpHeaders.encodeBasicAuth(username, password, charset);
return (request, next) ->
next.exchange(ClientRequest.from(request)
.headers(headers -> headers.setBasicAuth(encodedCredentials))
@@ -43,6 +43,7 @@ import static org.mockito.Mockito.mock;
* Tests for {@link ExchangeFilterFunctions}.
*
* @author Arjen Poutsma
* @author Kai Zander
*/
class ExchangeFilterFunctionsTests {
@@ -104,7 +105,7 @@ class ExchangeFilterFunctionsTests {
ExchangeFunction exchange = r -> {
assertThat(r.headers().containsHeader(HttpHeaders.AUTHORIZATION)).isTrue();
assertThat(r.headers().getFirst(HttpHeaders.AUTHORIZATION)).startsWith("Basic ");
assertThat(r.headers().getFirst(HttpHeaders.AUTHORIZATION)).isEqualTo("Basic Zm9vOmJhcg==");
return Mono.just(response);
};
@@ -114,6 +115,23 @@ class ExchangeFilterFunctionsTests {
assertThat(result).isEqualTo(response);
}
@Test // gh-36777
void basicAuthenticationUsernameAndUnicodePassword() {
ClientRequest request = ClientRequest.create(HttpMethod.GET, DEFAULT_URL).build();
ClientResponse response = mock();
ExchangeFunction exchange = r -> {
assertThat(r.headers().containsHeader(HttpHeaders.AUTHORIZATION)).isTrue();
assertThat(r.headers().getFirst(HttpHeaders.AUTHORIZATION)).isEqualTo("Basic Zm9vOvCfkqk=");
return Mono.just(response);
};
ExchangeFilterFunction auth = ExchangeFilterFunctions.basicAuthentication("foo", "\ud83d\udca9", StandardCharsets.UTF_8);
assertThat(request.headers().containsHeader(HttpHeaders.AUTHORIZATION)).isFalse();
ClientResponse result = auth.filter(request, exchange).block();
assertThat(result).isEqualTo(response);
}
@Test
void basicAuthenticationInvalidCharacters() {
ClientRequest request = ClientRequest.create(HttpMethod.GET, DEFAULT_URL).build();