Răsfoiți Sursa

Fix CsrfConfigurer default AccessDeniedHandler consistency

Fix when AccessDeniedHandler is specified per RequestMatcher on
ExceptionHandlingConfigurer.

This introduces evolutions on :
- CsrfConfigurer#getDefaultAccessDeniedHandler,
to retrieve an AccessDeniedHandler similar to the one used by
ExceptionHandlingConfigurer.
- OAuth2ResourceServerConfigurer#accessDeniedHandler, to continue to
handle CsrfException with the default AccessDeniedHandler implementation

Fixes: gh-6511
« Christophe 4 ani în urmă
părinte
comite
e85958f65c

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

@@ -237,8 +237,8 @@ public final class CsrfConfigurer<H extends HttpSecurityBuilder<H>>
 
 	/**
 	 * Gets the default {@link AccessDeniedHandler} from the
-	 * {@link ExceptionHandlingConfigurer#getAccessDeniedHandler()} or create a
-	 * {@link AccessDeniedHandlerImpl} if not available.
+	 * {@link ExceptionHandlingConfigurer#getAccessDeniedHandler(HttpSecurityBuilder)} or
+	 * create a {@link AccessDeniedHandlerImpl} if not available.
 	 * @param http the {@link HttpSecurityBuilder}
 	 * @return the {@link AccessDeniedHandler}
 	 */
@@ -247,7 +247,7 @@ public final class CsrfConfigurer<H extends HttpSecurityBuilder<H>>
 		ExceptionHandlingConfigurer<H> exceptionConfig = http.getConfigurer(ExceptionHandlingConfigurer.class);
 		AccessDeniedHandler handler = null;
 		if (exceptionConfig != null) {
-			handler = exceptionConfig.getAccessDeniedHandler();
+			handler = exceptionConfig.getAccessDeniedHandler(http);
 		}
 		if (handler == null) {
 			handler = new AccessDeniedHandlerImpl();

+ 8 - 1
config/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/resource/OAuth2ResourceServerConfigurer.java

@@ -18,6 +18,8 @@ package org.springframework.security.config.annotation.web.configurers.oauth2.se
 
 import java.util.Arrays;
 import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.Map;
 import java.util.function.Supplier;
 
 import jakarta.servlet.http.HttpServletRequest;
@@ -51,6 +53,9 @@ import org.springframework.security.oauth2.server.resource.web.DefaultBearerToke
 import org.springframework.security.oauth2.server.resource.web.access.BearerTokenAccessDeniedHandler;
 import org.springframework.security.web.AuthenticationEntryPoint;
 import org.springframework.security.web.access.AccessDeniedHandler;
+import org.springframework.security.web.access.AccessDeniedHandlerImpl;
+import org.springframework.security.web.access.DelegatingAccessDeniedHandler;
+import org.springframework.security.web.csrf.CsrfException;
 import org.springframework.security.web.util.matcher.AndRequestMatcher;
 import org.springframework.security.web.util.matcher.MediaTypeRequestMatcher;
 import org.springframework.security.web.util.matcher.NegatedRequestMatcher;
@@ -153,7 +158,9 @@ public final class OAuth2ResourceServerConfigurer<H extends HttpSecurityBuilder<
 
 	private OpaqueTokenConfigurer opaqueTokenConfigurer;
 
-	private AccessDeniedHandler accessDeniedHandler = new BearerTokenAccessDeniedHandler();
+	private AccessDeniedHandler accessDeniedHandler = new DelegatingAccessDeniedHandler(
+			new LinkedHashMap<>(Map.of(CsrfException.class, new AccessDeniedHandlerImpl())),
+			new BearerTokenAccessDeniedHandler());
 
 	private AuthenticationEntryPoint authenticationEntryPoint = new BearerTokenAuthenticationEntryPoint();
 

+ 30 - 1
config/src/test/java/org/springframework/security/config/annotation/web/configurers/CsrfConfigurerTests.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.
@@ -320,6 +320,17 @@ public class CsrfConfigurerTests {
 				any(HttpServletResponse.class), any());
 	}
 
+	@Test
+	public void getWhenCustomDefaultAccessDeniedHandlerForThenHandlerIsUsed() throws Exception {
+		DefaultAccessDeniedHandlerForConfig.DENIED_HANDLER = mock(AccessDeniedHandler.class);
+		DefaultAccessDeniedHandlerForConfig.MATCHER = mock(RequestMatcher.class);
+		given(DefaultAccessDeniedHandlerForConfig.MATCHER.matches(any())).willReturn(true);
+		this.spring.register(DefaultAccessDeniedHandlerForConfig.class, BasicController.class).autowire();
+		this.mvc.perform(post("/")).andExpect(status().isOk());
+		verify(DefaultAccessDeniedHandlerForConfig.DENIED_HANDLER).handle(any(HttpServletRequest.class),
+				any(HttpServletResponse.class), any());
+	}
+
 	@Test
 	public void loginWhenNoCsrfTokenThenRespondsWithForbidden() throws Exception {
 		this.spring.register(FormLoginConfig.class).autowire();
@@ -608,6 +619,24 @@ public class CsrfConfigurerTests {
 
 	}
 
+	@EnableWebSecurity
+	static class DefaultAccessDeniedHandlerForConfig extends WebSecurityConfigurerAdapter {
+
+		static AccessDeniedHandler DENIED_HANDLER;
+
+		static RequestMatcher MATCHER;
+
+		@Override
+		protected void configure(HttpSecurity http) throws Exception {
+			// @formatter:off
+			http
+				.exceptionHandling()
+					.defaultAccessDeniedHandlerFor(DENIED_HANDLER, MATCHER);
+			// @formatter:on
+		}
+
+	}
+
 	@EnableWebSecurity
 	static class FormLoginConfig extends WebSecurityConfigurerAdapter {