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 648bb11023..da4ffac8cf 100644 --- a/spring-web/src/main/java/org/springframework/http/HttpHeaders.java +++ b/spring-web/src/main/java/org/springframework/http/HttpHeaders.java @@ -742,7 +742,9 @@ public class HttpHeaders implements MultiValueMap, Serializable * @throws IllegalArgumentException if either {@code user} or * {@code password} contain characters that cannot be encoded to ISO-8859-1 * @since 5.1 + * @see #setBasicAuth(String) * @see #setBasicAuth(String, String, Charset) + * @see #encodeBasicAuth(String, String, Charset) * @see RFC 7617 */ public void setBasicAuth(String username, String password) { @@ -759,24 +761,33 @@ public class HttpHeaders implements MultiValueMap, Serializable * @throws IllegalArgumentException if {@code username} or {@code password} * contains characters that cannot be encoded to the given charset * @since 5.1 + * @see #setBasicAuth(String) + * @see #setBasicAuth(String, String) + * @see #encodeBasicAuth(String, String, Charset) * @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; - } + setBasicAuth(encodeBasicAuth(username, password, charset)); + } - 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 the value of the {@linkplain #AUTHORIZATION Authorization} header to + * Basic Authentication based on the given {@linkplain #encodeBasicAuth + * encoded credentials}. + *

Favor this method over {@link #setBasicAuth(String, String)} and + * {@link #setBasicAuth(String, String, Charset)} if you wish to cache the + * encoded credentials. + * @param encodedCredentials the encoded credentials + * @throws IllegalArgumentException if supplied credentials string is + * {@code null} or blank + * @since 5.2 + * @see #setBasicAuth(String, String) + * @see #setBasicAuth(String, String, Charset) + * @see #encodeBasicAuth(String, String, Charset) + * @see RFC 7617 + */ + public void setBasicAuth(String encodedCredentials) { + Assert.hasText(encodedCredentials, "'encodedCredentials' must not be null or blank"); set(AUTHORIZATION, "Basic " + encodedCredentials); } @@ -1781,6 +1792,41 @@ public class HttpHeaders implements MultiValueMap, Serializable .collect(Collectors.joining(", ", "[", "]")); } + /** + * Encode the given username and password into Basic Authentication credentials. + *

The encoded credentials returned by this method can be supplied to + * {@link #setBasicAuth(String)} to set the Basic Authentication header. + * @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}. + * @throws IllegalArgumentException if {@code username} or {@code password} + * contains characters that cannot be encoded to the given charset + * @since 5.2 + * @see #setBasicAuth(String) + * @see #setBasicAuth(String, String) + * @see #setBasicAuth(String, String, Charset) + * @see RFC 7617 + */ + public static String encodeBasicAuth(String username, String password, @Nullable Charset charset) { + Assert.notNull(username, "Username must not be null"); + Assert.doesNotContain(username, ":", "Username must not contain a colon"); + 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)); + return new String(encodedBytes, charset); + } + // Package-private: used in ResponseCookie static String formatDate(long date) { Instant instant = Instant.ofEpochMilli(date); diff --git a/spring-web/src/main/java/org/springframework/http/client/support/BasicAuthenticationInterceptor.java b/spring-web/src/main/java/org/springframework/http/client/support/BasicAuthenticationInterceptor.java index 42a4c18c21..71f09d6483 100644 --- a/spring-web/src/main/java/org/springframework/http/client/support/BasicAuthenticationInterceptor.java +++ b/spring-web/src/main/java/org/springframework/http/client/support/BasicAuthenticationInterceptor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * Copyright 2002-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -25,25 +25,21 @@ import org.springframework.http.client.ClientHttpRequestExecution; import org.springframework.http.client.ClientHttpRequestInterceptor; import org.springframework.http.client.ClientHttpResponse; import org.springframework.lang.Nullable; -import org.springframework.util.Assert; /** * {@link ClientHttpRequestInterceptor} to apply a given HTTP Basic Authentication - * username/password pair, unless a custom Authorization header has been set before. + * username/password pair, unless a custom {@code Authorization} header has + * already been set. * * @author Juergen Hoeller + * @author Sam Brannen * @since 5.1.1 * @see HttpHeaders#setBasicAuth * @see HttpHeaders#AUTHORIZATION */ public class BasicAuthenticationInterceptor implements ClientHttpRequestInterceptor { - private final String username; - - private final String password; - - @Nullable - private final Charset charset; + private final String encodedCredentials; /** @@ -52,6 +48,7 @@ public class BasicAuthenticationInterceptor implements ClientHttpRequestIntercep * @param username the username to use * @param password the password to use * @see HttpHeaders#setBasicAuth(String, String) + * @see HttpHeaders#encodeBasicAuth(String, String, Charset) */ public BasicAuthenticationInterceptor(String username, String password) { this(username, password, null); @@ -64,12 +61,10 @@ public class BasicAuthenticationInterceptor implements ClientHttpRequestIntercep * @param password the password to use * @param charset the charset to use * @see HttpHeaders#setBasicAuth(String, String, Charset) + * @see HttpHeaders#encodeBasicAuth(String, String, Charset) */ public BasicAuthenticationInterceptor(String username, String password, @Nullable Charset charset) { - Assert.doesNotContain(username, ":", "Username must not contain a colon"); - this.username = username; - this.password = password; - this.charset = charset; + this.encodedCredentials = HttpHeaders.encodeBasicAuth(username, password, charset); } @@ -79,7 +74,7 @@ public class BasicAuthenticationInterceptor implements ClientHttpRequestIntercep HttpHeaders headers = request.getHeaders(); if (!headers.containsKey(HttpHeaders.AUTHORIZATION)) { - headers.setBasicAuth(this.username, this.password, this.charset); + headers.setBasicAuth(this.encodedCredentials); } return execution.execute(request, body); }