소스 검색

Logout is 204 for XMLHttpRequest

Fixes gh-3997
Rob Winch 9 년 전
부모
커밋
edb7ef567a

+ 10 - 3
config/src/main/java/org/springframework/security/config/annotation/web/configurers/HttpBasicConfigurer.java

@@ -38,6 +38,7 @@ import org.springframework.security.web.authentication.www.BasicAuthenticationFi
 import org.springframework.security.web.util.matcher.AndRequestMatcher;
 import org.springframework.security.web.util.matcher.MediaTypeRequestMatcher;
 import org.springframework.security.web.util.matcher.NegatedRequestMatcher;
+import org.springframework.security.web.util.matcher.OrRequestMatcher;
 import org.springframework.security.web.util.matcher.RequestHeaderRequestMatcher;
 import org.springframework.security.web.util.matcher.RequestMatcher;
 import org.springframework.web.accept.ContentNegotiationStrategy;
@@ -78,6 +79,10 @@ import org.springframework.web.accept.HeaderContentNegotiationStrategy;
  */
 public final class HttpBasicConfigurer<B extends HttpSecurityBuilder<B>> extends
 		AbstractHttpConfigurer<HttpBasicConfigurer<B>, B> {
+
+	private static final RequestHeaderRequestMatcher X_REQUESTED_WITH = new RequestHeaderRequestMatcher("X-Requested-With",
+			"XMLHttpRequest");
+
 	private static final String DEFAULT_REALM = "Realm";
 
 	private AuthenticationEntryPoint authenticationEntryPoint;
@@ -93,8 +98,7 @@ public final class HttpBasicConfigurer<B extends HttpSecurityBuilder<B>> extends
 		realmName(DEFAULT_REALM);
 
 		LinkedHashMap<RequestMatcher, AuthenticationEntryPoint> entryPoints = new LinkedHashMap<RequestMatcher, AuthenticationEntryPoint>();
-		entryPoints.put(new RequestHeaderRequestMatcher("X-Requested-With",
-				"XMLHttpRequest"), new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED));
+		entryPoints.put(X_REQUESTED_WITH, new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED));
 
 		DelegatingAuthenticationEntryPoint defaultEntryPoint = new DelegatingAuthenticationEntryPoint(
 				entryPoints);
@@ -157,6 +161,7 @@ public final class HttpBasicConfigurer<B extends HttpSecurityBuilder<B>> extends
 		if (contentNegotiationStrategy == null) {
 			contentNegotiationStrategy = new HeaderContentNegotiationStrategy();
 		}
+
 		MediaTypeRequestMatcher restMatcher = new MediaTypeRequestMatcher(
 				contentNegotiationStrategy, MediaType.APPLICATION_ATOM_XML,
 				MediaType.APPLICATION_FORM_URLENCODED, MediaType.APPLICATION_JSON,
@@ -167,9 +172,11 @@ public final class HttpBasicConfigurer<B extends HttpSecurityBuilder<B>> extends
 		RequestMatcher notHtmlMatcher = new NegatedRequestMatcher(
 				new MediaTypeRequestMatcher(contentNegotiationStrategy,
 						MediaType.TEXT_HTML));
-		RequestMatcher preferredMatcher = new AndRequestMatcher(
+		RequestMatcher restNotHtmlMatcher = new AndRequestMatcher(
 				Arrays.<RequestMatcher>asList(notHtmlMatcher, restMatcher));
 
+		RequestMatcher preferredMatcher = new OrRequestMatcher(Arrays.asList(X_REQUESTED_WITH, restNotHtmlMatcher));
+
 		registerDefaultEntryPoint(http, preferredMatcher);
 		registerDefaultLogoutSuccessHandler(http, preferredMatcher);
 	}

+ 2 - 2
config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/ExceptionHandlingConfigurerTests.groovy

@@ -92,7 +92,7 @@ class ExceptionHandlingConfigurerTests extends BaseSpringSpec {
 			DelegatingAuthenticationEntryPoint delegateEntryPoint = findFilter(ExceptionTranslationFilter).authenticationEntryPoint
 		then:
 			def entryPoints = delegateEntryPoint.entryPoints.keySet() as List
-			entryPoints[0].requestMatchers[1].contentNegotiationStrategy.class == HeaderContentNegotiationStrategy
+			entryPoints[0].requestMatchers[1].requestMatchers[1].contentNegotiationStrategy.class == HeaderContentNegotiationStrategy
 			entryPoints[1].requestMatchers[1].contentNegotiationStrategy.class == HeaderContentNegotiationStrategy
 	}
 
@@ -138,7 +138,7 @@ class ExceptionHandlingConfigurerTests extends BaseSpringSpec {
 		then:
 			def entryPoints = delegateEntryPoint.entryPoints.keySet() as List
 			entryPoints[0].requestMatchers[1].contentNegotiationStrategy == OverrideContentNegotiationStrategySharedObjectConfig.CNS
-			entryPoints[1].requestMatchers[1].contentNegotiationStrategy == OverrideContentNegotiationStrategySharedObjectConfig.CNS
+			entryPoints[1].requestMatchers[1].requestMatchers[1].contentNegotiationStrategy == OverrideContentNegotiationStrategySharedObjectConfig.CNS
 	}
 
 	def "Override ContentNegotiationStrategy with @Bean"() {

+ 19 - 0
config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/LogoutConfigurerTests.groovy

@@ -232,4 +232,23 @@ class LogoutConfigurerTests extends BaseSpringSpec {
 	@EnableWebSecurity
 	static class LogoutHandlerContentNegotiationForChrome extends WebSecurityConfigurerAdapter {
 	}
+
+	// gh-3997
+	def "LogoutConfigurer for XMLHttpRequest is 204"() {
+		setup:
+			loadConfig(LogoutXMLHttpRequestConfig)
+		when:
+			login()
+			request.method = 'POST'
+			request.servletPath = '/logout'
+			request.addHeader('Accept', 'text/html,application/json')
+			request.addHeader('X-Requested-With', 'XMLHttpRequest')
+			springSecurityFilterChain.doFilter(request,response,chain)
+		then:
+			response.status == 204
+	}
+
+	@EnableWebSecurity
+	static class LogoutXMLHttpRequestConfig extends WebSecurityConfigurerAdapter {
+	}
 }