DanielKWinsor
DanielKWinsor

Reputation: 125

How can one add a filter across all HttpSecurity Configurers?

As a solutions architect, I want to add a filter to all spring security filter chains in the current application context, regardless of how or where they are declared, so that this filter cannot be missed by a developer who forgot to add it.

Suppose that different developers are adding in some custom security to address different logical concerns specific to their filter. There can be 1 or more WebSecurityConfigurerAdapters

public class DeveloperWebSecurityConfiguration extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .mvcMatcher("/forgottenFilter/**")
                //.addFilter(reallyImportantFilter)
                .authorizeRequests()
                .anyRequest().access("developerDefinedCriteria");
    }
}

I myself could declare a WebSecurityConfigurerAdapter that adds a filter to the HttpSecurity, but this is treated as an individual catch-all filter chain.

public class FrameworkWebSecurityConfiguration extends WebSecurityConfigurerAdapter {
    @Autowired
    Filter reallyImportantFilter;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.addFilter(reallyImportantFilter);
    }
}

In effect, I have not added my filter to the developer's chain, but rather created an entirely separate chain that interferes with the mapping of other filter chains.

DefaultSecurityFilterChain INFO: Creating filter chain: any request, [... ReallyImportantFilter@77ce8bc5 ...]

DefaultSecurityFilterChain INFO: Creating filter chain: Mvc [pattern='/forgottenFilter/**'] [...]

Is there an out of the box way to configure such a filter? If not, what patterns can be applied to avoid a filter from being forgotten?

Upvotes: 0

Views: 1304

Answers (2)

Vijay Nandwana
Vijay Nandwana

Reputation: 2644

You can create a generic filter and register it in the servlet context. Here is a sample code for your reference:

public class ReallyImportantFilter extends GenericFilterBean {

    private static final Logger LOGGER = LoggerFactory.getLogger(ReallyImportantFilter.class);

    @Override
    public void doFilter(final ServletRequest request, final ServletResponse response, final FilterChain chain) {
        try {
            chain.doFilter(request, response);
        } catch (final Throwable t) {
            LOGGER.error("Encountered unhandled exception.", t);

            final HttpServletResponse httpResponse = (HttpServletResponse) response;

            httpResponse.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());

Register this filter and assign appropriate order precedence.

<bean id="reallyImportantFilterRegistration" class="org.springframework.boot.web.servlet.FilterRegistrationBean">
    <property name="filter">
        <bean class="com.test.web.errorhandling.ReallyImportantFilter"/>
    </property>
    <property name="order">
        <util:constant static-field="org.springframework.core.Ordered.HIGHEST_PRECEDENCE"/>
    </property>
</bean>

Upvotes: 0

NatFar
NatFar

Reputation: 2220

A feature of WebSecurityConfigurerAdapter came to mind. The javadoc states:

Will automatically apply the result of looking up AbstractHttpConfigurer from SpringFactoriesLoader to allow developers to extend the defaults. To do this, you must create a class that extends AbstractHttpConfigurer and then create a file in the classpath at "META-INF/spring.factories" that looks something like:

org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer = sample.MyClassThatExtendsAbstractHttpConfigurer

For example, in your case, you can create your own implementation of AbstractHttpConfigurer where you add the very important filter:

package sample;
// ...
public class CustomSecurityConfigurer<H extends HttpSecurityBuilder<H>>
    extends AbstractHttpConfigurer<CustomSecurityConfigurer<H>, H> {

    @Override
    public void init(H http) { }
    
    @Override
    public void configure(H http) {
        // add your own filter... for example:
        http.addFilterAfter(new ImportantFilter(), LogoutFilter.class);
    }
}

... and add this line to the spring.factories in the META-INF folder:

org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer=sample.CustomSecurityConfigurer

In WebSecurityConfigurerAdapter, this configurer is added to the "list" of configurers (think CsrfConfigurer, LogoutConfigurer, ...) that will build the HttpSecurity, and ultimately the corresponding filter chain.

If you have multiple WebSecurityConfigurerAdapters, each one will apply this configurer, so you'll end up configuring every filter chain that's created from HttpSecurity.

Upvotes: 3

Related Questions