Explorar o código

Add HttpSecurity.addFilterAt (#3809)

Fixes gh-3784
Rob Winch %!s(int64=9) %!d(string=hai) anos
pai
achega
a7fb6d2e58

+ 17 - 0
config/src/main/java/org/springframework/security/config/annotation/web/builders/FilterComparator.java

@@ -149,6 +149,23 @@ final class FilterComparator implements Comparator<Filter>, Serializable {
 		put(filter, position + 1);
 	}
 
+	/**
+	 * Registers a {@link Filter} to exist at a particular {@link Filter} position
+	 * @param filter the {@link Filter} to register
+	 * @param atFilter the {@link Filter} that is already registered and that
+	 * {@code filter} should be placed at.
+	 */
+	public void registerAt(Class<? extends Filter> filter,
+			Class<? extends Filter> atFilter) {
+		Integer position = getOrder(atFilter);
+		if (position == null) {
+			throw new IllegalArgumentException(
+					"Cannot register after unregistered Filter " + atFilter);
+		}
+
+		put(filter, position);
+	}
+
 	/**
 	 * Registers a {@link Filter} to exist before a particular {@link Filter} that is
 	 * already registered.

+ 19 - 0
config/src/main/java/org/springframework/security/config/annotation/web/builders/HttpSecurity.java

@@ -1025,6 +1025,25 @@ public final class HttpSecurity extends
 		return this;
 	}
 
+	/**
+	 * Adds the Filter at the location of the specified Filter class. For example, if you
+	 * want the filter CustomFilter to be registered in the same position as
+	 * {@link UsernamePasswordAuthenticationFilter}, you can invoke:
+	 *
+	 * <pre>
+	 * addFilterAt(new CustomFilter(), UsernamePasswordAuthenticationFilter.class)
+	 * </pre>
+	 *
+	 * @param filter the Filter to register
+	 * @param atFilter the location of another {@link Filter} that is already registered
+	 * (i.e. known) with Spring Security.
+	 * @return the {@link HttpSecurity} for further customizations
+	 */
+	public HttpSecurity addFilterAt(Filter filter, Class<? extends Filter> atFilter) {
+		this.comparitor.registerAt(filter.getClass(), atFilter);
+		return addFilter(filter);
+	}
+
 	/**
 	 * Allows specifying which {@link HttpServletRequest} instances this
 	 * {@link HttpSecurity} will be invoked on. This method allows for easily invoking the

+ 28 - 0
config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/NamespaceHttpCustomFilterTests.groovy

@@ -17,6 +17,7 @@ package org.springframework.security.config.annotation.web.configurers;
 
 import java.io.IOException;
 
+import javax.servlet.FilterChain
 import javax.servlet.ServletException;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
@@ -55,6 +56,7 @@ import org.springframework.security.web.servletapi.SecurityContextHolderAwareReq
 import org.springframework.security.web.util.matcher.AntPathRequestMatcher
 import org.springframework.security.web.util.matcher.AnyRequestMatcher;
 import org.springframework.security.web.util.matcher.RequestMatcher
+import org.springframework.web.filter.OncePerRequestFilter
 
 import spock.lang.Ignore;
 
@@ -127,7 +129,26 @@ public class NamespaceHttpCustomFilterTests extends BaseSpringSpec {
 				// if not, use addFilterBefore or addFilterAfter
 				.addFilter(new CustomFilter())
 		}
+	}
 
+	def "http/custom-filter@position at"() {
+		when:
+		loadConfig(CustomFilterPositionAtConfig)
+		then:
+		filterChain().filters.collect { it.class } == [OtherCustomFilter]
+	}
+
+	@Configuration
+	static class CustomFilterPositionAtConfig extends BaseWebConfig {
+		CustomFilterPositionAtConfig() {
+			// do not add the default filters to make testing easier
+			super(true)
+		}
+
+		protected void configure(HttpSecurity http) {
+			http
+				.addFilterAt(new OtherCustomFilter(), UsernamePasswordAuthenticationFilter.class)
+		}
 	}
 
 	def "http/custom-filter no AuthenticationManager in HttpSecurity"() {
@@ -159,6 +180,13 @@ public class NamespaceHttpCustomFilterTests extends BaseSpringSpec {
 	}
 
 	static class CustomFilter extends UsernamePasswordAuthenticationFilter {}
+	static class OtherCustomFilter extends OncePerRequestFilter {
+		protected void doFilterInternal(
+				HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
+		throws ServletException, IOException {
+			filterChain.doFilter(request,response);
+		}
+	}
 
 	static class CustomAuthenticationManager implements AuthenticationManager {
 		public Authentication authenticate(Authentication authentication)