How to order multiple <http> elements in Spring Security distributed over multiple files

java, spring, spring-security

Solution

At this time there is no way to break up Spring Security's XML configuration and specify an order short of ensuring your XML is loaded in the correct order. One way to achieve this would be to do the following:

Create a /some-common-config-path/security.xml configuration that is loaded by the common configuration that imports the two configurations (that are not in the common config location) in the correct order:

<import resource="/not-common-config-path/api-security-config.xml"/>
<import resource="/not-common-config-path/web-security-config.xml"/>

You can do this in Spring Security 3.2 with Java Configuration and using the @Order annotation. As demonstrated in the documentation:

@Configuration
@EnableWebSecurity
public class MultiHttpSecurityConfig {
    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) { 
        auth
            .inMemoryAuthentication()
                .withUser("user").password("password").roles("USER");
    }

    @Configuration
    @Order(1)                                                        
    public static class ApiWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
        protected void configure(HttpSecurity http) throws Exception {
            http
                .antMatcher("/api/**")                               
                .authorizeRequests()
                    .anyRequest().hasRole("ADMIN")
                    .and()
                .httpBasic();
        }
    }

    @Configuration                                                   
    public static class FormLoginWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {

        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http
                .authorizeRequests()
                    .anyRequest().authenticated()
                    .and()
                .formLogin();
        }
    }
}

You could also break up these configurations if you so choose.

To automatically pick up the Java Configuration, you could easily add classpath scanning to your setup. For example if you are using XML centric configuration and all your Java Configuration was in the package com.example.config you could add:

<!-- 
    enable processing of annotations such as @Autowired and @Configuration
    You may already have annotation-config
 -->
<context:annotation-config/>
<!-- Add any Java Configuration -->
<context:component-scan base-package="com.example.config"/>

For more details about XML and Java Configuration read Combining Java and XML Configuration from the reference.

Problem

In Spring Security one can specify multiple `<http>` configurations that result in multiple SecurityFilterChains. I use that feature for securing a Rest API different than the normal web app. Both, web app and rest api are developed in different modules (maven artifacts). Spring configs are collected by wildcard pattern throughout whole classpath (`classpath*:/some-common-config-path/*.xml`). Security-Config for web app in `web-security-config.xml`: ``` <beans:beans xmlns="http://www.springframework.org/schema/security" xmlns:beans="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://cxf.apache.org/configuration/beans http://cxf.apache.org/schemas/configuration/cxf-beans.xsd http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.1.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd"> <!-- Security Config for web app --> <http use-expressions="true" auto-config="false" entry-point-ref="loginEntryPoint"> ... </http> <!-- Security Config for static resources --> <http pattern="/static/**" security="none" /> ... </beans:beans> ``` Security-Config for the Rest API in `api-security-config.xml`: ``` <beans:beans xmlns="http://www.springframework.org/schema/security" xmlns:beans="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://cxf.apache.org/configuration/beans http://cxf.apache.org/schemas/configuration/cxf-beans.xsd http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.1.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd"> <http pattern="/api/**" use-expressions="true" create-session="stateless" authentication-manager-ref="apiAuthManager" entry-point-ref="restAuthenticationEntryPoint"> <intercept-url pattern="/api/**" access="hasRole('REST_API')" /> <http-basic /> </http> ... </beans:beans> ``` The problem is, that the order of both modules within the classpath is unpredictable and so is the order of the parsing of the config files. In my particular case `api-security-config.xml` is read after `web-security-config.xml` and the application context startup fails with the following exception: java.lang.IllegalArgumentException: A universal match pattern ('/**') is defined before other patterns in the filter chain, causing them to be ignored. Please check the ordering in your namespace or FilterChainProxy bean configuration So what I would do is somehow specify the order of the `<http>` elements, so that the specific configs are parsed before the most universal one. Is there any possibility to do that?

Original source