浏览代码

Bearer Token Server-side Errors Return 500

Closes gh-9395
Josh Cummings 4 年之前
父节点
当前提交
ccb3b02888

+ 11 - 13
config/src/test/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/resource/OAuth2ResourceServerConfigurerTests.java

@@ -1,5 +1,5 @@
 /*
- * Copyright 2002-2020 the original author or authors.
+ * Copyright 2002-2021 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.
@@ -78,6 +78,7 @@ import org.springframework.security.authentication.AuthenticationEventPublisher;
 import org.springframework.security.authentication.AuthenticationManager;
 import org.springframework.security.authentication.AuthenticationManagerResolver;
 import org.springframework.security.authentication.AuthenticationProvider;
+import org.springframework.security.authentication.AuthenticationServiceException;
 import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
 import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
 import org.springframework.security.config.annotation.web.builders.HttpSecurity;
@@ -104,7 +105,6 @@ import org.springframework.security.oauth2.jwt.BadJwtException;
 import org.springframework.security.oauth2.jwt.Jwt;
 import org.springframework.security.oauth2.jwt.JwtClaimNames;
 import org.springframework.security.oauth2.jwt.JwtDecoder;
-import org.springframework.security.oauth2.jwt.JwtException;
 import org.springframework.security.oauth2.jwt.JwtTimestampValidator;
 import org.springframework.security.oauth2.jwt.NimbusJwtDecoder;
 import org.springframework.security.oauth2.jwt.TestJwts;
@@ -260,26 +260,24 @@ public class OAuth2ResourceServerConfigurerTests {
 	}
 
 	@Test
-	public void getWhenUsingDefaultsWithBadJwkEndpointThenInvalidToken() throws Exception {
+	public void getWhenUsingDefaultsWithBadJwkEndpointThen500() throws Exception {
 		this.spring.register(RestOperationsConfig.class, DefaultConfig.class).autowire();
 		mockRestOperations("malformed");
 		String token = this.token("ValidNoScopes");
 		// @formatter:off
-		this.mvc.perform(get("/").with(bearerToken(token)))
-				.andExpect(status().isUnauthorized())
-				.andExpect(header().string("WWW-Authenticate", "Bearer"));
+		assertThatExceptionOfType(AuthenticationServiceException.class)
+				.isThrownBy(() -> this.mvc.perform(get("/").with(bearerToken(token))));
 		// @formatter:on
 	}
 
 	@Test
-	public void getWhenUsingDefaultsWithUnavailableJwkEndpointThenInvalidToken() throws Exception {
+	public void getWhenUsingDefaultsWithUnavailableJwkEndpointThen500() throws Exception {
 		this.spring.register(WebServerConfig.class, JwkSetUriConfig.class).autowire();
 		this.web.shutdown();
 		String token = this.token("ValidNoScopes");
 		// @formatter:off
-		this.mvc.perform(get("/").with(bearerToken(token)))
-				.andExpect(status().isUnauthorized())
-				.andExpect(header().string("WWW-Authenticate", "Bearer"));
+		assertThatExceptionOfType(AuthenticationServiceException.class)
+				.isThrownBy(() -> this.mvc.perform(get("/").with(bearerToken(token))));
 		// @formatter:on
 	}
 
@@ -825,7 +823,7 @@ public class OAuth2ResourceServerConfigurerTests {
 	public void requestWhenRealmNameConfiguredThenUsesOnUnauthenticated() throws Exception {
 		this.spring.register(RealmNameConfiguredOnEntryPoint.class, JwtDecoderConfig.class).autowire();
 		JwtDecoder decoder = this.spring.getContext().getBean(JwtDecoder.class);
-		given(decoder.decode(anyString())).willThrow(JwtException.class);
+		given(decoder.decode(anyString())).willThrow(BadJwtException.class);
 		// @formatter:off
 		this.mvc.perform(get("/authenticated").with(bearerToken("invalid_token")))
 				.andExpect(status().isUnauthorized())
@@ -1092,7 +1090,7 @@ public class OAuth2ResourceServerConfigurerTests {
 	public void requestWhenBasicAndResourceServerEntryPointsThenMatchedByRequest() throws Exception {
 		this.spring.register(BasicAndResourceServerConfig.class, JwtDecoderConfig.class).autowire();
 		JwtDecoder decoder = this.spring.getContext().getBean(JwtDecoder.class);
-		given(decoder.decode(anyString())).willThrow(JwtException.class);
+		given(decoder.decode(anyString())).willThrow(BadJwtException.class);
 		// @formatter:off
 		this.mvc.perform(get("/authenticated").with(httpBasic("some", "user")))
 				.andExpect(status().isUnauthorized())
@@ -1110,7 +1108,7 @@ public class OAuth2ResourceServerConfigurerTests {
 	public void requestWhenFormLoginAndResourceServerEntryPointsThenSessionCreatedByRequest() throws Exception {
 		this.spring.register(FormAndResourceServerConfig.class, JwtDecoderConfig.class).autowire();
 		JwtDecoder decoder = this.spring.getContext().getBean(JwtDecoder.class);
-		given(decoder.decode(anyString())).willThrow(JwtException.class);
+		given(decoder.decode(anyString())).willThrow(BadJwtException.class);
 		// @formatter:off
 		MvcResult result = this.mvc.perform(get("/authenticated")
 				.header("Accept", "text/html"))

+ 12 - 13
config/src/test/java/org/springframework/security/config/http/OAuth2ResourceServerBeanDefinitionParserTests.java

@@ -1,5 +1,5 @@
 /*
- * Copyright 2002-2020 the original author or authors.
+ * Copyright 2002-2021 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.
@@ -69,6 +69,7 @@ import org.springframework.http.MediaType;
 import org.springframework.http.RequestEntity;
 import org.springframework.http.ResponseEntity;
 import org.springframework.security.authentication.AuthenticationManagerResolver;
+import org.springframework.security.authentication.AuthenticationServiceException;
 import org.springframework.security.config.http.OAuth2ResourceServerBeanDefinitionParser.JwtBeanDefinitionParser;
 import org.springframework.security.config.http.OAuth2ResourceServerBeanDefinitionParser.OpaqueTokenBeanDefinitionParser;
 import org.springframework.security.config.test.SpringTestRule;
@@ -76,10 +77,10 @@ import org.springframework.security.oauth2.core.OAuth2Error;
 import org.springframework.security.oauth2.core.OAuth2TokenValidator;
 import org.springframework.security.oauth2.core.OAuth2TokenValidatorResult;
 import org.springframework.security.oauth2.jose.TestKeys;
+import org.springframework.security.oauth2.jwt.BadJwtException;
 import org.springframework.security.oauth2.jwt.Jwt;
 import org.springframework.security.oauth2.jwt.JwtClaimNames;
 import org.springframework.security.oauth2.jwt.JwtDecoder;
-import org.springframework.security.oauth2.jwt.JwtException;
 import org.springframework.security.oauth2.jwt.NimbusJwtDecoder;
 import org.springframework.security.oauth2.jwt.TestJwts;
 import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken;
@@ -168,26 +169,24 @@ public class OAuth2ResourceServerBeanDefinitionParserTests {
 	}
 
 	@Test
-	public void getWhenBadJwkEndpointThenInvalidToken() throws Exception {
+	public void getWhenBadJwkEndpointThen500() throws Exception {
 		this.spring.configLocations(xml("JwtRestOperations"), xml("Jwt")).autowire();
 		mockRestOperations("malformed");
 		String token = this.token("ValidNoScopes");
 		// @formatter:off
-		this.mvc.perform(get("/").header("Authorization", "Bearer " + token))
-				.andExpect(status().isUnauthorized())
-				.andExpect(header().string("WWW-Authenticate", "Bearer"));
+		assertThatExceptionOfType(AuthenticationServiceException.class)
+				.isThrownBy(() -> this.mvc.perform(get("/").header("Authorization", "Bearer " + token)));
 		// @formatter:on
 	}
 
 	@Test
-	public void getWhenUnavailableJwkEndpointThenInvalidToken() throws Exception {
+	public void getWhenUnavailableJwkEndpointThen500() throws Exception {
 		this.spring.configLocations(xml("WebServer"), xml("JwkSetUri")).autowire();
 		this.web.shutdown();
 		String token = this.token("ValidNoScopes");
 		// @formatter:off
-		this.mvc.perform(get("/").header("Authorization", "Bearer " + token))
-				.andExpect(status().isUnauthorized())
-				.andExpect(header().string("WWW-Authenticate", "Bearer"));
+		assertThatExceptionOfType(AuthenticationServiceException.class)
+				.isThrownBy(() -> this.mvc.perform(get("/").header("Authorization", "Bearer " + token)));
 		// @formatter:on
 	}
 
@@ -529,7 +528,7 @@ public class OAuth2ResourceServerBeanDefinitionParserTests {
 	public void requestWhenRealmNameConfiguredThenUsesOnUnauthenticated() throws Exception {
 		this.spring.configLocations(xml("MockJwtDecoder"), xml("AuthenticationEntryPoint")).autowire();
 		JwtDecoder decoder = this.spring.getContext().getBean(JwtDecoder.class);
-		Mockito.when(decoder.decode(anyString())).thenThrow(JwtException.class);
+		Mockito.when(decoder.decode(anyString())).thenThrow(BadJwtException.class);
 		// @formatter:off
 		this.mvc.perform(get("/authenticated").header("Authorization", "Bearer invalid_token"))
 				.andExpect(status().isUnauthorized())
@@ -736,7 +735,7 @@ public class OAuth2ResourceServerBeanDefinitionParserTests {
 		// different from DSL
 		this.spring.configLocations(xml("MockJwtDecoder"), xml("BasicAndResourceServer")).autowire();
 		JwtDecoder decoder = this.spring.getContext().getBean(JwtDecoder.class);
-		given(decoder.decode(anyString())).willThrow(JwtException.class);
+		given(decoder.decode(anyString())).willThrow(BadJwtException.class);
 		// @formatter:off
 		this.mvc.perform(get("/authenticated").with(httpBasic("some", "user")))
 				.andExpect(status().isUnauthorized())
@@ -755,7 +754,7 @@ public class OAuth2ResourceServerBeanDefinitionParserTests {
 		// different from DSL
 		this.spring.configLocations(xml("MockJwtDecoder"), xml("FormAndResourceServer")).autowire();
 		JwtDecoder decoder = this.spring.getContext().getBean(JwtDecoder.class);
-		given(decoder.decode(anyString())).willThrow(JwtException.class);
+		given(decoder.decode(anyString())).willThrow(BadJwtException.class);
 		MvcResult result = this.mvc.perform(get("/authenticated")).andExpect(status().isUnauthorized()).andReturn();
 		assertThat(result.getRequest().getSession(false)).isNotNull();
 		// @formatter:off

+ 8 - 3
oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/web/BearerTokenAuthenticationFilter.java

@@ -1,5 +1,5 @@
 /*
- * Copyright 2002-2019 the original author or authors.
+ * Copyright 2002-2021 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.
@@ -27,6 +27,7 @@ import org.springframework.core.log.LogMessage;
 import org.springframework.security.authentication.AuthenticationDetailsSource;
 import org.springframework.security.authentication.AuthenticationManager;
 import org.springframework.security.authentication.AuthenticationManagerResolver;
+import org.springframework.security.authentication.AuthenticationServiceException;
 import org.springframework.security.core.Authentication;
 import org.springframework.security.core.AuthenticationException;
 import org.springframework.security.core.context.SecurityContext;
@@ -66,8 +67,12 @@ public final class BearerTokenAuthenticationFilter extends OncePerRequestFilter
 
 	private AuthenticationEntryPoint authenticationEntryPoint = new BearerTokenAuthenticationEntryPoint();
 
-	private AuthenticationFailureHandler authenticationFailureHandler = (request, response,
-			exception) -> this.authenticationEntryPoint.commence(request, response, exception);
+	private AuthenticationFailureHandler authenticationFailureHandler = (request, response, exception) -> {
+		if (exception instanceof AuthenticationServiceException) {
+			throw exception;
+		}
+		this.authenticationEntryPoint.commence(request, response, exception);
+	};
 
 	/**
 	 * Construct a {@code BearerTokenAuthenticationFilter} using the provided parameter(s)

+ 14 - 1
oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/web/BearerTokenAuthenticationFilterTests.java

@@ -1,5 +1,5 @@
 /*
- * Copyright 2002-2019 the original author or authors.
+ * Copyright 2002-2021 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.
@@ -34,6 +34,7 @@ import org.springframework.mock.web.MockHttpServletRequest;
 import org.springframework.mock.web.MockHttpServletResponse;
 import org.springframework.security.authentication.AuthenticationManager;
 import org.springframework.security.authentication.AuthenticationManagerResolver;
+import org.springframework.security.authentication.AuthenticationServiceException;
 import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
 import org.springframework.security.oauth2.server.resource.BearerTokenAuthenticationToken;
 import org.springframework.security.oauth2.server.resource.BearerTokenError;
@@ -42,6 +43,7 @@ import org.springframework.security.web.AuthenticationEntryPoint;
 import org.springframework.security.web.authentication.AuthenticationFailureHandler;
 
 import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
 import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.BDDMockito.given;
@@ -154,6 +156,17 @@ public class BearerTokenAuthenticationFilterTests {
 		verify(this.authenticationFailureHandler).onAuthenticationFailure(this.request, this.response, exception);
 	}
 
+	@Test
+	public void doFilterWhenAuthenticationServiceExceptionThenRethrows() {
+		AuthenticationServiceException exception = new AuthenticationServiceException("message");
+		given(this.bearerTokenResolver.resolve(this.request)).willReturn("token");
+		given(this.authenticationManager.authenticate(any())).willThrow(exception);
+		BearerTokenAuthenticationFilter filter = addMocks(
+				new BearerTokenAuthenticationFilter(this.authenticationManager));
+		assertThatExceptionOfType(AuthenticationServiceException.class)
+				.isThrownBy(() -> filter.doFilter(this.request, this.response, this.filterChain));
+	}
+
 	@Test
 	public void setAuthenticationEntryPointWhenNullThenThrowsException() {
 		BearerTokenAuthenticationFilter filter = new BearerTokenAuthenticationFilter(this.authenticationManager);