Browse Source

Support multiple RequestRejectedHandler beans

Closes gh-10603
Adam Ostrožlík 3 năm trước cách đây
mục cha
commit
4ea57f3e3f

+ 13 - 0
config/src/main/java/org/springframework/security/config/annotation/web/builders/WebSecurity.java

@@ -279,6 +279,19 @@ public final class WebSecurity extends AbstractConfiguredSecurityBuilder<Filter,
 		return this;
 	}
 
+	/**
+	 * Sets the handler to handle
+	 * {@link org.springframework.security.web.firewall.RequestRejectedException}
+	 * @param requestRejectedHandler
+	 * @return the {@link WebSecurity} for further customizations
+	 * @since 5.7
+	 */
+	public WebSecurity requestRejectedHandler(RequestRejectedHandler requestRejectedHandler) {
+		Assert.notNull(requestRejectedHandler, "requestRejectedHandler cannot be null");
+		this.requestRejectedHandler = requestRejectedHandler;
+		return this;
+	}
+
 	@Override
 	protected Filter performBuild() throws Exception {
 		Assert.state(!this.securityFilterChainBuilders.isEmpty(),

+ 24 - 0
config/src/test/java/org/springframework/security/config/annotation/web/builders/WebSecurityTests.java

@@ -17,6 +17,7 @@
 package org.springframework.security.config.annotation.web.builders;
 
 import javax.servlet.http.HttpServletResponse;
+import javax.servlet.ServletException;
 
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.BeforeEach;
@@ -24,6 +25,7 @@ import org.junit.jupiter.api.Test;
 
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.context.annotation.Configuration;
+import org.springframework.http.HttpStatus;
 import org.springframework.mock.web.MockFilterChain;
 import org.springframework.mock.web.MockHttpServletRequest;
 import org.springframework.mock.web.MockHttpServletResponse;
@@ -32,6 +34,7 @@ import org.springframework.security.config.annotation.authentication.builders.Au
 import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
 import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
 import org.springframework.security.web.FilterChainProxy;
+import org.springframework.security.web.firewall.HttpStatusRequestRejectedHandler;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RestController;
 import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
@@ -39,6 +42,8 @@ import org.springframework.web.servlet.config.annotation.EnableWebMvc;
 import org.springframework.web.servlet.config.annotation.PathMatchConfigurer;
 import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
 
+import java.io.IOException;
+
 import static org.assertj.core.api.Assertions.assertThat;
 
 /**
@@ -92,6 +97,15 @@ public class WebSecurityTests {
 		assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_UNAUTHORIZED);
 	}
 
+	@Test
+	public void requestRejectedHandlerInvoked() throws ServletException, IOException {
+		loadConfig(RequestRejectedHandlerConfig.class);
+		this.request.setServletPath("/spring");
+		this.request.setRequestURI("/spring/\u0019path");
+		this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
+		assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_BAD_REQUEST);
+	}
+
 	@Test
 	public void ignoringMvcMatcherServletPath() throws Exception {
 		loadConfig(MvcMatcherServletPathConfig.class, LegacyMvcMatchingConfig.class);
@@ -223,4 +237,14 @@ public class WebSecurityTests {
 
 	}
 
+	@EnableWebSecurity
+	static class RequestRejectedHandlerConfig extends WebSecurityConfigurerAdapter {
+
+		@Override
+		public void configure(WebSecurity web) throws Exception {
+			web.requestRejectedHandler(new HttpStatusRequestRejectedHandler(HttpStatus.BAD_REQUEST.value()));
+		}
+
+	}
+
 }

+ 57 - 0
web/src/main/java/org/springframework/security/web/firewall/CompositeRequestRejectedHandler.java

@@ -0,0 +1,57 @@
+/*
+ * Copyright 2002-2022 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.
+ * You may obtain a copy of the License at
+ *
+ *      https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.security.web.firewall;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import org.springframework.util.Assert;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * A {@link RequestRejectedHandler} that delegates to several other
+ * {@link RequestRejectedHandler}s.
+ *
+ * @author Adam Ostrožlík
+ * @since 5.7
+ */
+public final class CompositeRequestRejectedHandler implements RequestRejectedHandler {
+
+	private final List<RequestRejectedHandler> requestRejectedhandlers;
+
+	/**
+	 * Creates a new instance.
+	 * @param requestRejectedhandlers the {@link RequestRejectedHandler} instances to
+	 * handle {@link org.springframework.security.web.firewall.RequestRejectedException}
+	 */
+	public CompositeRequestRejectedHandler(RequestRejectedHandler... requestRejectedhandlers) {
+		Assert.notEmpty(requestRejectedhandlers, "requestRejectedhandlers cannot be empty");
+		this.requestRejectedhandlers = Arrays.asList(requestRejectedhandlers);
+	}
+
+	@Override
+	public void handle(HttpServletRequest request, HttpServletResponse response,
+			RequestRejectedException requestRejectedException) throws IOException, ServletException {
+		for (RequestRejectedHandler requestRejectedhandler : requestRejectedhandlers) {
+			requestRejectedhandler.handle(request, response, requestRejectedException);
+		}
+	}
+
+}

+ 43 - 0
web/src/test/java/org/springframework/security/web/firewall/CompositeRequestRejectedHandlerTests.java

@@ -0,0 +1,43 @@
+/*
+ * 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.
+ * You may obtain a copy of the License at
+ *
+ *      https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.security.web.firewall;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import org.junit.jupiter.api.Test;
+
+import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
+import static org.mockito.Mockito.mock;
+
+public class CompositeRequestRejectedHandlerTests {
+
+	@Test
+	void compositeRequestRejectedHandlerRethrowsTheException() {
+		RequestRejectedException requestRejectedException = new RequestRejectedException("rejected");
+		DefaultRequestRejectedHandler sut = new DefaultRequestRejectedHandler();
+		CompositeRequestRejectedHandler crrh = new CompositeRequestRejectedHandler(sut);
+		assertThatExceptionOfType(RequestRejectedException.class).isThrownBy(() -> crrh
+				.handle(mock(HttpServletRequest.class), mock(HttpServletResponse.class), requestRejectedException))
+				.withMessage("rejected");
+	}
+
+	@Test
+	void compositeRequestRejectedHandlerForbidsEmptyHandlers() {
+		assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(CompositeRequestRejectedHandler::new);
+	}
+
+}