瀏覽代碼

ServerBearerTokenAuthenticationConverter Handles Empty Tokens

Previously ServerBearerTokenAuthenticationConverter would throw an
IllegalArgumentException when the access token in a URI was empty String.
It also incorrectly provided HttpStatus.BAD_REQUEST for an empty String
access token in the headers.

This changes ServerBearerTokenAuthenticationConverter to consistently
throw a OAuth2AuthenticationException with an HttpStatus.UNAUTHORIZED

Fixes gh-7011
Rob Winch 6 年之前
父節點
當前提交
6f5a443175

+ 17 - 7
oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/web/server/ServerBearerTokenAuthenticationConverter.java

@@ -50,8 +50,14 @@ public class ServerBearerTokenAuthenticationConverter
 	private boolean allowUriQueryParameter = false;
 
 	public Mono<Authentication> convert(ServerWebExchange exchange) {
-		return Mono.justOrEmpty(this.token(exchange.getRequest()))
-			.map(BearerTokenAuthenticationToken::new);
+		return Mono.justOrEmpty(token(exchange.getRequest()))
+			.map(token -> {
+				if (token.isEmpty()) {
+					BearerTokenError error = invalidTokenError();
+					throw new OAuth2AuthenticationException(error);
+				}
+				return new BearerTokenAuthenticationToken(token);
+			});
 	}
 
 	private String token(ServerHttpRequest request) {
@@ -90,11 +96,8 @@ public class ServerBearerTokenAuthenticationConverter
 		if (StringUtils.startsWithIgnoreCase(authorization, "bearer")) {
 			Matcher matcher = authorizationPattern.matcher(authorization);
 
-			if ( !matcher.matches() ) {
-				BearerTokenError error = new BearerTokenError(BearerTokenErrorCodes.INVALID_TOKEN,
-						HttpStatus.BAD_REQUEST,
-						"Bearer token is malformed",
-						"https://tools.ietf.org/html/rfc6750#section-3.1");
+			if (!matcher.matches() ) {
+				BearerTokenError error = invalidTokenError();
 				throw new OAuth2AuthenticationException(error);
 			}
 
@@ -103,6 +106,13 @@ public class ServerBearerTokenAuthenticationConverter
 		return null;
 	}
 
+	private static BearerTokenError invalidTokenError() {
+		return new BearerTokenError(BearerTokenErrorCodes.INVALID_TOKEN,
+							HttpStatus.UNAUTHORIZED,
+							"Bearer token is malformed",
+							"https://tools.ietf.org/html/rfc6750#section-3.1");
+	}
+
 	private boolean isParameterTokenSupportedForRequest(ServerHttpRequest request) {
 		return this.allowUriQueryParameter && HttpMethod.GET.equals(request.getMethod());
 	}

+ 36 - 0
oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/web/server/ServerBearerTokenAuthenticationConverterTests.java

@@ -19,15 +19,19 @@ package org.springframework.security.oauth2.server.resource.web.server;
 import org.junit.Before;
 import org.junit.Test;
 import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpStatus;
 import org.springframework.mock.http.server.reactive.MockServerHttpRequest;
 import org.springframework.mock.web.server.MockServerWebExchange;
 import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
 import org.springframework.security.oauth2.server.resource.BearerTokenAuthenticationToken;
+import org.springframework.security.oauth2.server.resource.BearerTokenError;
+import org.springframework.security.oauth2.server.resource.BearerTokenErrorCodes;
 
 import java.util.Base64;
 
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.assertj.core.api.Assertions.assertThatCode;
+import static org.assertj.core.api.Assertions.catchThrowableOfType;
 
 /**
  * @author Rob Winch
@@ -52,6 +56,21 @@ public class ServerBearerTokenAuthenticationConverterTests {
 		assertThat(convertToToken(request).getToken()).isEqualTo(TEST_TOKEN);
 	}
 
+	// gh-7011
+	@Test
+	public void resolveWhenValidHeaderIsEmptyStringThenTokenIsResolved() {
+		MockServerHttpRequest.BaseBuilder<?> request = MockServerHttpRequest
+				.get("/")
+				.header(HttpHeaders.AUTHORIZATION, "Bearer ");
+
+		OAuth2AuthenticationException expected = catchThrowableOfType(() -> convertToToken(request),
+				OAuth2AuthenticationException.class);
+		BearerTokenError error = (BearerTokenError) expected.getError();
+		assertThat(error.getErrorCode()).isEqualTo(BearerTokenErrorCodes.INVALID_TOKEN);
+		assertThat(error.getUri()).isEqualTo("https://tools.ietf.org/html/rfc6750#section-3.1");
+		assertThat(error.getHttpStatus()).isEqualTo(HttpStatus.UNAUTHORIZED);
+	}
+
 	@Test
 	public void resolveWhenLowercaseHeaderIsPresentThenTokenIsResolved() {
 		MockServerHttpRequest.BaseBuilder<?> request = MockServerHttpRequest
@@ -123,6 +142,23 @@ public class ServerBearerTokenAuthenticationConverterTests {
 		assertThat(convertToToken(request).getToken()).isEqualTo(TEST_TOKEN);
 	}
 
+	// gh-7011
+	@Test
+	public void resolveWhenQueryParameterIsEmptyAndSupportedThenOAuth2AuthenticationException() {
+		this.converter.setAllowUriQueryParameter(true);
+
+		MockServerHttpRequest.BaseBuilder<?> request = MockServerHttpRequest
+				.get("/")
+				.queryParam("access_token", "");
+
+		OAuth2AuthenticationException expected = catchThrowableOfType(() -> convertToToken(request),
+			OAuth2AuthenticationException.class);
+		BearerTokenError error = (BearerTokenError) expected.getError();
+		assertThat(error.getErrorCode()).isEqualTo(BearerTokenErrorCodes.INVALID_TOKEN);
+		assertThat(error.getUri()).isEqualTo("https://tools.ietf.org/html/rfc6750#section-3.1");
+		assertThat(error.getHttpStatus()).isEqualTo(HttpStatus.UNAUTHORIZED);
+	}
+
 	@Test
 	public void resolveWhenQueryParameterIsPresentAndNotSupportedThenTokenIsNotResolved() {
 		MockServerHttpRequest.BaseBuilder<?> request = MockServerHttpRequest