From a7fb6d2e58d22767958d7f57ab124371164d93d8 Mon Sep 17 00:00:00 2001 From: Rob Winch Date: Wed, 13 Apr 2016 15:01:25 -0500 Subject: [PATCH] Add HttpSecurity.addFilterAt (#3809) Fixes gh-3784 --- .../web/builders/FilterComparator.java | 17 +++++++++++ .../annotation/web/builders/HttpSecurity.java | 19 +++++++++++++ .../NamespaceHttpCustomFilterTests.groovy | 28 +++++++++++++++++++ 3 files changed, 64 insertions(+) diff --git a/config/src/main/java/org/springframework/security/config/annotation/web/builders/FilterComparator.java b/config/src/main/java/org/springframework/security/config/annotation/web/builders/FilterComparator.java index 0f456107b3..2b8c584e94 100644 --- a/config/src/main/java/org/springframework/security/config/annotation/web/builders/FilterComparator.java +++ b/config/src/main/java/org/springframework/security/config/annotation/web/builders/FilterComparator.java @@ -149,6 +149,23 @@ final class FilterComparator implements Comparator, 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 filter, + Class 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. diff --git a/config/src/main/java/org/springframework/security/config/annotation/web/builders/HttpSecurity.java b/config/src/main/java/org/springframework/security/config/annotation/web/builders/HttpSecurity.java index 0b5066385f..0d68542790 100644 --- a/config/src/main/java/org/springframework/security/config/annotation/web/builders/HttpSecurity.java +++ b/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: + * + *
+	 * addFilterAt(new CustomFilter(), UsernamePasswordAuthenticationFilter.class)
+	 * 
+ * + * @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 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 diff --git a/config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/NamespaceHttpCustomFilterTests.groovy b/config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/NamespaceHttpCustomFilterTests.groovy index bebf5b0ba5..bc272c2425 100644 --- a/config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/NamespaceHttpCustomFilterTests.groovy +++ b/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)