Use configurable charset in ServerHttpBasicAuthenticationConverter
Closes gh-10903
This commit is contained in:
		
							parent
							
								
									c38c722473
								
							
						
					
					
						commit
						1b29c43a11
					
				| 
						 | 
				
			
			@ -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;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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()));
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue