Use configurable charset in ServerHttpBasicAuthenticationConverter

Closes gh-10903
This commit is contained in:
David Kirstein 2022-02-25 20:03:13 +01:00 committed by Steve Riesenberg
parent c38c722473
commit 1b29c43a11
No known key found for this signature in database
GPG Key ID: 5F311AB48A55D521
2 changed files with 51 additions and 4 deletions

View File

@ -16,6 +16,8 @@
package org.springframework.security.web.server;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.function.Function;
@ -25,6 +27,7 @@ import org.springframework.http.HttpHeaders;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
@ -43,6 +46,8 @@ public class ServerHttpBasicAuthenticationConverter implements Function<ServerWe
public static final String BASIC = "Basic ";
private Charset credentialsCharset = StandardCharsets.UTF_8;
@Override
@Deprecated
public Mono<Authentication> apply(ServerWebExchange exchange) {
@ -51,9 +56,8 @@ public class ServerHttpBasicAuthenticationConverter implements Function<ServerWe
if (!StringUtils.startsWithIgnoreCase(authorization, "basic ")) {
return Mono.empty();
}
String credentials = (authorization.length() <= BASIC.length()) ? ""
: authorization.substring(BASIC.length(), authorization.length());
String decoded = new String(base64Decode(credentials));
String credentials = (authorization.length() <= BASIC.length()) ? "" : authorization.substring(BASIC.length());
String decoded = new String(base64Decode(credentials), this.credentialsCharset);
String[] parts = decoded.split(":", 2);
if (parts.length != 2) {
return Mono.empty();
@ -70,4 +74,13 @@ public class ServerHttpBasicAuthenticationConverter implements Function<ServerWe
}
}
public Charset getCredentialsCharset() {
return this.credentialsCharset;
}
public void setCredentialsCharset(Charset credentialsCharset) {
Assert.notNull(credentialsCharset, "credentialsCharset cannot be null");
this.credentialsCharset = credentialsCharset;
}
}

View File

@ -16,6 +16,8 @@
package org.springframework.security.web.server.authentication;
import java.nio.charset.StandardCharsets;
import org.junit.jupiter.api.Test;
import reactor.core.publisher.Mono;
@ -62,7 +64,7 @@ public class ServerHttpBasicAuthenticationConverterTests {
}
@Test
public void applyWhenNoSemicolonThenEmpty() {
public void applyWhenNoColonThenEmpty() {
Mono<Authentication> result = apply(this.request.header(HttpHeaders.AUTHORIZATION, "Basic dXNlcg=="));
assertThat(result.block()).isNull();
}
@ -104,6 +106,38 @@ public class ServerHttpBasicAuthenticationConverterTests {
assertThat(result.block()).isNull();
}
@Test
public void applyWhenNonAsciiThenAuthentication() {
Mono<Authentication> result = apply(
this.request.header(HttpHeaders.AUTHORIZATION, "Basic w7xzZXI6cGFzc3fDtnJk"));
UsernamePasswordAuthenticationToken authentication = result.cast(UsernamePasswordAuthenticationToken.class)
.block();
assertThat(authentication.getPrincipal()).isEqualTo("üser");
assertThat(authentication.getCredentials()).isEqualTo("passwörd");
}
@Test
public void applyWhenIsoOnlyAsciiThenAuthentication() {
this.converter.setCredentialsCharset(StandardCharsets.ISO_8859_1);
Mono<Authentication> result = apply(
this.request.header(HttpHeaders.AUTHORIZATION, "Basic dXNlcjpwYXNzd29yZA=="));
UsernamePasswordAuthenticationToken authentication = result.cast(UsernamePasswordAuthenticationToken.class)
.block();
assertThat(authentication.getPrincipal()).isEqualTo("user");
assertThat(authentication.getCredentials()).isEqualTo("password");
}
@Test
public void applyWhenIsoNonAsciiThenAuthentication() {
this.converter.setCredentialsCharset(StandardCharsets.ISO_8859_1);
Mono<Authentication> result = apply(
this.request.header(HttpHeaders.AUTHORIZATION, "Basic /HNlcjpwYXNzd/ZyZA=="));
UsernamePasswordAuthenticationToken authentication = result.cast(UsernamePasswordAuthenticationToken.class)
.block();
assertThat(authentication.getPrincipal()).isEqualTo("üser");
assertThat(authentication.getCredentials()).isEqualTo("passwörd");
}
private Mono<Authentication> apply(MockServerHttpRequest.BaseBuilder<?> request) {
return this.converter.convert(MockServerWebExchange.from(this.request.build()));
}