Browse Source

Add Basic Authentication Consumer

Issue: SPR-16913
pull/1871/head
Arjen Poutsma 8 years ago
parent
commit
6bcf6ffb06
  1. 89
      spring-web/src/main/java/org/springframework/http/HttpHeaders.java
  2. 25
      spring-web/src/test/java/org/springframework/http/HttpHeadersTests.java

89
spring-web/src/main/java/org/springframework/http/HttpHeaders.java

@ -20,6 +20,8 @@ import java.io.Serializable; @@ -20,6 +20,8 @@ import java.io.Serializable;
import java.net.InetSocketAddress;
import java.net.URI;
import java.nio.charset.Charset;
import java.nio.charset.CharsetEncoder;
import java.nio.charset.StandardCharsets;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.time.Instant;
@ -28,6 +30,7 @@ import java.time.ZonedDateTime; @@ -28,6 +30,7 @@ import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.util.ArrayList;
import java.util.Base64;
import java.util.Collection;
import java.util.Collections;
import java.util.EnumSet;
@ -38,6 +41,8 @@ import java.util.List; @@ -38,6 +41,8 @@ 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;
@ -1564,4 +1569,88 @@ public class HttpHeaders implements MultiValueMap<String, String>, Serializable @@ -1564,4 +1569,88 @@ public class HttpHeaders implements MultiValueMap<String, String>, Serializable
return (headers.readOnly ? headers : new HttpHeaders(headers, true));
}
/**
* Returns 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)}.
* <p>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
*/
public static Consumer<HttpHeaders> basicAuthenticationConsumer(String username,String password) {
return basicAuthenticationConsumer(() -> username, () -> password);
}
/**
* Returns 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)}.
* <p>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
*/
public static Consumer<HttpHeaders> basicAuthenticationConsumer(Supplier<String> usernameSupplier,
Supplier<String> passwordSupplier) {
Assert.notNull(usernameSupplier, "Username Supplier must not be null");
Assert.notNull(passwordSupplier, "Password Supplier must not be null");
return new BasicAuthenticationConsumer(usernameSupplier, passwordSupplier);
}
/**
* @see #basicAuthenticationConsumer
*/
private static class BasicAuthenticationConsumer implements Consumer<HttpHeaders> {
private final Supplier<String> usernameSupplier;
private final Supplier<String> passwordSupplier;
public BasicAuthenticationConsumer(Supplier<String> usernameSupplier,
Supplier<String> 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");
}
}
}
}

25
spring-web/src/test/java/org/springframework/http/HttpHeadersTests.java

@ -26,6 +26,7 @@ import java.time.ZoneId; @@ -26,6 +26,7 @@ import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Base64;
import java.util.Calendar;
import java.util.Collections;
import java.util.EnumSet;
@ -33,12 +34,13 @@ import java.util.GregorianCalendar; @@ -33,12 +34,13 @@ 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;
import static java.time.format.DateTimeFormatter.*;
import static org.hamcrest.Matchers.*;
import static java.time.format.DateTimeFormatter.RFC_1123_DATE_TIME;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.*;
/**
@ -529,4 +531,23 @@ public class HttpHeadersTests { @@ -529,4 +531,23 @@ public class HttpHeadersTests {
assertTrue(headers.getFirstZonedDateTime(HttpHeaders.DATE).isEqual(date));
}
@Test
public void basicAuthenticationConsumer() throws Exception {
String username = "foo";
String password = "bar";
Consumer<HttpHeaders> consumer =
HttpHeaders.basicAuthenticationConsumer(username, password);
HttpHeaders headers = new HttpHeaders();
assertFalse(headers.containsKey(HttpHeaders.AUTHORIZATION));
consumer.accept(headers);
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));
}
}

Loading…
Cancel
Save