Polish BasicAuthenticationConverter

This reverts to the old behavior from BasicAuthenticationFilter.
Specifically, if a token has an empty password, it still parses a username
and an empty String password.

Issue gh-7025
This commit is contained in:
Rob Winch 2019-08-02 08:14:05 -05:00
parent d157125c8e
commit ad2f999c25
2 changed files with 31 additions and 25 deletions

View File

@ -17,7 +17,7 @@ package org.springframework.security.web.authentication.www;
import static org.springframework.http.HttpHeaders.AUTHORIZATION; import static org.springframework.http.HttpHeaders.AUTHORIZATION;
import java.io.UnsupportedEncodingException; import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.Base64; import java.util.Base64;
@ -25,7 +25,6 @@ import javax.servlet.http.HttpServletRequest;
import org.springframework.security.authentication.AuthenticationDetailsSource; import org.springframework.security.authentication.AuthenticationDetailsSource;
import org.springframework.security.authentication.BadCredentialsException; import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.InternalAuthenticationServiceException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.web.authentication.AuthenticationConverter; import org.springframework.security.web.authentication.AuthenticationConverter;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
@ -47,7 +46,7 @@ public class BasicAuthenticationConverter implements AuthenticationConverter {
private AuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource; private AuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource;
private String credentialsCharset = StandardCharsets.UTF_8.name(); private Charset credentialsCharset = StandardCharsets.UTF_8;
public BasicAuthenticationConverter() { public BasicAuthenticationConverter() {
this(new WebAuthenticationDetailsSource()); this(new WebAuthenticationDetailsSource());
@ -58,16 +57,16 @@ public class BasicAuthenticationConverter implements AuthenticationConverter {
this.authenticationDetailsSource = authenticationDetailsSource; this.authenticationDetailsSource = authenticationDetailsSource;
} }
public String getCredentialsCharset() { public Charset getCredentialsCharset() {
return credentialsCharset; return this.credentialsCharset;
} }
public void setCredentialsCharset(String credentialsCharset) { public void setCredentialsCharset(Charset credentialsCharset) {
this.credentialsCharset = credentialsCharset; this.credentialsCharset = credentialsCharset;
} }
public AuthenticationDetailsSource<HttpServletRequest, ?> getAuthenticationDetailsSource() { public AuthenticationDetailsSource<HttpServletRequest, ?> getAuthenticationDetailsSource() {
return authenticationDetailsSource; return this.authenticationDetailsSource;
} }
public void setAuthenticationDetailsSource( public void setAuthenticationDetailsSource(
@ -88,34 +87,29 @@ public class BasicAuthenticationConverter implements AuthenticationConverter {
return null; return null;
} }
byte[] base64Token = header.substring(6).getBytes(); byte[] base64Token = header.substring(6).getBytes(StandardCharsets.UTF_8);
byte[] decoded; byte[] decoded;
try { try {
decoded = Base64.getDecoder().decode(base64Token); decoded = Base64.getDecoder().decode(base64Token);
} catch (IllegalArgumentException e) { }
throw new BadCredentialsException("Failed to decode basic authentication token"); catch (IllegalArgumentException e) {
throw new BadCredentialsException(
"Failed to decode basic authentication token");
} }
String token; String token = new String(decoded, getCredentialsCharset(request));
try {
token = new String(decoded, getCredentialsCharset(request));
} catch (UnsupportedEncodingException e) {
throw new InternalAuthenticationServiceException(e.getMessage(), e);
}
String[] tokens = token.split(":"); int delim = token.indexOf(":");
if (tokens.length != 2) {
if (delim == -1) {
throw new BadCredentialsException("Invalid basic authentication token"); throw new BadCredentialsException("Invalid basic authentication token");
} }
UsernamePasswordAuthenticationToken result = new UsernamePasswordAuthenticationToken(token.substring(0, delim), token.substring(delim + 1));
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(tokens[0], result.setDetails(this.authenticationDetailsSource.buildDetails(request));
tokens[1]); return result;
authentication.setDetails(authenticationDetailsSource.buildDetails(request));
return authentication;
} }
protected String getCredentialsCharset(HttpServletRequest request) { protected Charset getCredentialsCharset(HttpServletRequest request) {
return getCredentialsCharset(); return getCredentialsCharset();
} }

View File

@ -99,4 +99,16 @@ public class BasicAuthenticationConverterTests {
converter.convert(request); converter.convert(request);
} }
@Test
public void convertWhenEmptyPassword() {
String token = "rod:";
MockHttpServletRequest request = new MockHttpServletRequest();
request.addHeader("Authorization", "Basic " + new String(Base64.encodeBase64(token.getBytes())));
UsernamePasswordAuthenticationToken authentication = converter.convert(request);
verify(authenticationDetailsSource).buildDetails(any());
assertThat(authentication).isNotNull();
assertThat(authentication.getName()).isEqualTo("rod");
assertThat(authentication.getCredentials()).isEqualTo("");
}
} }