3

I created a custom Spring security filter chain and I want to exclude all url beginning with "/health".

here is my filter configuration:

@Override
public void configure(WebSecurity web) throws Exception {
    web
            .ignoring()
            .antMatchers("/health");
}

@Override
protected void configure(HttpSecurity http) throws Exception {
    http
            .exceptionHandling()
            .authenticationEntryPoint(ssoEntryPoint());
    http
            .authorizeRequests()

            .antMatchers("/images/**").permitAll()
            .antMatchers("/scripts/**").permitAll()
            .antMatchers("/styles/**").permitAll()
            .antMatchers("/vendor/**").permitAll()
            .antMatchers("/views/**").permitAll()
            .antMatchers("/index.html").permitAll()
            .antMatchers("/api/**").authenticated();

    http    // login configuration
            .addFilterAfter(ssoSpringSecurityFilter(), BasicAuthenticationFilter.class);

    http    //logout configuration
            .logout()
            .logoutSuccessHandler(logoutHandler());

    http.csrf().disable();

}

when I start my application i have this trace:

  2016-01-29 12:59:23.729  INFO 10572 --- [ost-startStop-1] o.s.s.web.DefaultSecurityFilterChain     : Creating filter chain: Ant [pattern='/health'], []
  2016-01-29 12:59:23.814 DEBUG 10572 --- [ost-startStop-1] edFilterInvocationSecurityMetadataSource : Adding web access control expression 'permitAll', for Ant [pattern='/images/**']
  2016-01-29 12:59:23.816 DEBUG 10572 --- [ost-startStop-1] edFilterInvocationSecurityMetadataSource : Adding web access control expression 'permitAll', for Ant [pattern='/modules/**']
  2016-01-29 12:59:23.816 DEBUG 10572 --- [ost-startStop-1] edFilterInvocationSecurityMetadataSource : Adding web access control expression 'permitAll', for Ant [pattern='/scripts/**']
  2016-01-29 12:59:23.816 DEBUG 10572 --- [ost-startStop-1] edFilterInvocationSecurityMetadataSource : Adding web access control expression 'permitAll', for Ant [pattern='/styles/**']
  2016-01-29 12:59:23.816 DEBUG 10572 --- [ost-startStop-1] edFilterInvocationSecurityMetadataSource : Adding web access control expression 'permitAll', for Ant [pattern='/vendor/**']
  2016-01-29 12:59:23.816 DEBUG 10572 --- [ost-startStop-1] edFilterInvocationSecurityMetadataSource : Adding web access control expression 'permitAll', for Ant [pattern='/views/**']
  2016-01-29 12:59:23.816 DEBUG 10572 --- [ost-startStop-1] edFilterInvocationSecurityMetadataSource : Adding web access control expression 'permitAll', for Ant [pattern='/index.html']
  2016-01-29 12:59:23.816 DEBUG 10572 --- [ost-startStop-1] edFilterInvocationSecurityMetadataSource : Adding web access control expression 'authenticated', for Ant [pattern='/api/**']

When I invoque my service my this url:

  https://localhost:9999/health

I have this stack trace:

  2016-01-29 13:05:34.076  INFO 10572 --- [nio-9999-exec-4] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring FrameworkServlet 'dispatcherServlet'
  2016-01-29 13:05:34.076  INFO 10572 --- [nio-9999-exec-4] o.s.web.servlet.DispatcherServlet        : FrameworkServlet 'dispatcherServlet': initialization started
  2016-01-29 13:05:34.121  INFO 10572 --- [nio-9999-exec-4] o.s.web.servlet.DispatcherServlet        : FrameworkServlet 'dispatcherServlet': initialization completed in 45 ms
  2016-01-29 13:05:34.136 DEBUG 10572 --- [nio-9999-exec-4] o.s.s.w.u.matcher.AntPathRequestMatcher  : Checking match of request : '/health'; against '/css/**'
  2016-01-29 13:05:34.136 DEBUG 10572 --- [nio-9999-exec-4] o.s.s.w.u.matcher.AntPathRequestMatcher  : Checking match of request : '/health'; against '/js/**'
  2016-01-29 13:05:34.136 DEBUG 10572 --- [nio-9999-exec-4] o.s.s.w.u.matcher.AntPathRequestMatcher  : Checking match of request : '/health'; against '/images/**'
  2016-01-29 13:05:34.137 DEBUG 10572 --- [nio-9999-exec-4] o.s.s.w.u.matcher.AntPathRequestMatcher  : Checking match of request : '/health'; against '/**/favicon.ico'
  2016-01-29 13:05:34.137 DEBUG 10572 --- [nio-9999-exec-4] o.s.s.w.u.matcher.AntPathRequestMatcher  : Checking match of request : '/health'; against '/error'
  2016-01-29 13:05:34.137 DEBUG 10572 --- [nio-9999-exec-4] o.s.s.w.u.matcher.AntPathRequestMatcher  : Checking match of request : '/health'; against '/health'
  2016-01-29 13:05:34.137 DEBUG 10572 --- [nio-9999-exec-4] o.s.security.web.FilterChainProxy        : /health has an empty filter list

What is mean health has an empty filter list?

youssef Liouene
  • 873
  • 6
  • 15
  • 28

2 Answers2

3

I had the same problem, and I could never get the "web.ignoring().antMatchers(...)" to work. My custom filter kept getting called regardless.

There is a way you can force the filter to not affect given URLs. I found this Answer: https://stackoverflow.com/a/19985323/7206367 which gave a possible solution intended to allow filter-specific exclusions, but it can also be used to force the URL exclusion override. Below is my adaptation for this problem.

Below are the implementations of the support methods, but then all you have to do is just add this to HttpSecurity instead of your custom filter:

new DelegateRequestMatchingFilter(SECURITY_EXCLUSION_MATCHER, myCustomFilter);

The support code:

@Configuration
@EnableWebSecurity
@EnableAutoConfiguration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    ...

    private static final RequestMatcher SECURITY_EXCLUSION_MATCHER;
    static {
        String[] urls = new String[] {
                "/login",
                "/refreshToken",
                "/health",
                "/ping"
        };

        //Build Matcher List
        LinkedList<RequestMatcher> matcherList = new LinkedList<>();
        for (String url : urls) {
            matcherList.add(new AntPathRequestMatcher(url));
        }

        //Link Matchers in "OR" config.
        SECURITY_EXCLUSION_MATCHER = new OrRequestMatcher(matcherList);
    }

    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().requestMatchers(SECURITY_EXCLUSION_MATCHER);
    }

    @Override
    protected void configure(HttpSecurity httpSecurity) throws Exception {
        httpSecurity.authorizeRequests()
                .requestMatchers(SECURITY_EXCLUSION_MATCHER).permitAll()

    }

    ...

    /**
     * Since the "web.ignoring()..." is not stopping the Custom Filter from acting on the ignored urls,
     * this delegation class will force a check on the Security Exclusion list before allowing the
     * Custom Filter to process anything. 
     */
    public static class DelegateRequestMatchingFilter implements Filter {
        private Filter delegate;
        private RequestMatcher ignoredRequests;

        public DelegateRequestMatchingFilter(RequestMatcher matcher, Filter delegate) {
            this.ignoredRequests = matcher;
            this.delegate = delegate;
        }

        public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException {
            HttpServletRequest request = (HttpServletRequest) req;
            if(ignoredRequests.matches(request)) {
                chain.doFilter(req,resp);
            } else {
                delegate.doFilter(req,resp,chain);
            }
        }

        public void init(FilterConfig filterConfig) throws ServletException {
            delegate.init(filterConfig);
        }

        public void destroy() {
            delegate.destroy();
        }
    }
}

Or

import javax.servlet.http.HttpServletRequest;

import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;

/**
 * <p>
 * This matcher will return all matches which match <cdoe>baselineMatches</code>, if
 * and only if, those matches are not matched by <code>ignoreMatches</code>.
 * </p>
 * 
 * <p>
 * This matcher first checks <code>ignoreMatches</code>. If a given request is found
 * as a match to <code>ignoreMatches</code>, this matcher will return false (not a match).
 * If a given request does not match <code>ignoreMatches</code>, then this matcher returns
 * whether or not that request matches <code>baselineMatches</code>.
 * </p>
 * 
 * Effectively:<br>
 * <code>
 * if (ignoreMatches.matches(request)) {
 *      return false;
 * } else {
 *      return baselineMatches.matches(request);
 * }
 * </code>
 * @param baselineMatches Matcher used to determine a request match.
 * @param ignoreMatches Matcher used to exclude matches from the baselineMatcher.
 */
public class AExceptBRequestMatcher implements RequestMatcher {
    private RequestMatcher baselineMatches;
    private RequestMatcher ignoreMatches;

    public AExceptBRequestMatcher(String baselineMatches, RequestMatcher ignoreMatches) {
        this(new AntPathRequestMatcher(baselineMatches), ignoreMatches);
    }

    public AExceptBRequestMatcher(RequestMatcher baselineMatches, RequestMatcher ignoreMatches) {
        this.baselineMatches = baselineMatches;
        this.ignoreMatches = ignoreMatches;
    }

    @Override
    public boolean matches(HttpServletRequest request) {
        if (ignoreMatches.matches(request)) {
            return false;
        } else {
            return baselineMatches.matches(request);
        }
    }
}

And then, in your filter constructor where you call something like:

public MyCustomFilter() {
    super("/**");
}

Instead you can take in a RequestMatcher matching whatever you want to ignore/exclude (ex: the SECURITY_EXCLUSION_MATCHER from the first example), and do this:

public MyCustomFilter(RequestMatcher excludeMatcher) {
    super(new AExceptBRequestMatcher("/**", excludeMatcher));
}
Yurelle
  • 348
  • 2
  • 13
2

When you are doing this:

web.ignoring().antMatchers("/health");

Is the same as spring configuration xml security="none".

It means that this url will not be secured and returning a empty filter list means that spring won't send the request throw his filter because there is not filters.. Meaning unsecured url

EDIT: I am not sure what are the diffrences but this works for sure:

http.antMatchers("/health").permitAll();

This should be instead of .ignoring() and should be put under the HttpSecurity method with all the rest

Aviad
  • 1,539
  • 1
  • 9
  • 24