How to represent the Spring Security "custom-filter" using Java configuration?

49,126

Solution 1

A few issues you may need to keep in mind:

  1. Your filter needs to be added before the standard UsernamePasswordAuthenticationFilter

    
    http.addFilterBefore(customUsernamePasswordAuthenticationFilter(),
            UsernamePasswordAuthenticationFilter.class)
    
  2. If you extend UsernamePasswordAuthenticationFilter your filter will return immediately without doing anything unless you set a RequestMatcher

    
    myAuthFilter.setRequiresAuthenticationRequestMatcher(
        new AntPathRequestMatcher("/login","POST"));
    
  3. All the configuration you do in http.formLogin().x().y().z() is applied to the standard UsernamePasswordAuthenticationFilter not the custom filter you build. You will need to configure it manually yourself. My auth filter initialization looks like this:

    
    @Bean
    public MyAuthenticationFilter authenticationFilter() {
        MyAuthenticationFilter authFilter = new MyAuthenticationFilter();
        authFilter.setRequiresAuthenticationRequestMatcher(new AntPathRequestMatcher("/login","POST"));
        authFilter.setAuthenticationManager(authenticationManager);
        authFilter.setAuthenticationSuccessHandler(new MySuccessHandler("/app"));
        authFilter.setAuthenticationFailureHandler(new MyFailureHandler("/login?error=1"));
        authFilter.setUsernameParameter("username");
        authFilter.setPasswordParameter("password");
        return authFilter;
    }
    

Solution 2

I dont find any issue in this code. I think, your configuration is fine. Problem is somewhere else.I have similar code,

package com.programsji.config;

import java.util.ArrayList;
import java.util.List;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

import com.programsji.security.CustomAuthenticationProvider;
import com.programsji.security.CustomSuccessHandler;
import com.programsji.security.CustomUsernamePasswordAuthenticationFilter;

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers("/js/**", "/css/**", "/theme/**").and()
                .debug(true);
    }

    @Bean
    public CustomUsernamePasswordAuthenticationFilter customUsernamePasswordAuthenticationFilter()
            throws Exception {
        CustomUsernamePasswordAuthenticationFilter customUsernamePasswordAuthenticationFilter = new CustomUsernamePasswordAuthenticationFilter();
        customUsernamePasswordAuthenticationFilter
                .setAuthenticationManager(authenticationManagerBean());
        customUsernamePasswordAuthenticationFilter
                .setAuthenticationSuccessHandler(customSuccessHandler());
        return customUsernamePasswordAuthenticationFilter;
    }

    @Bean
    public CustomSuccessHandler customSuccessHandler() {
        CustomSuccessHandler customSuccessHandler = new CustomSuccessHandler();
        return customSuccessHandler;
    }

    @Bean
    public CustomAuthenticationProvider customAuthenticationProvider() {
        CustomAuthenticationProvider customAuthenticationProvider = new CustomAuthenticationProvider();
        return customAuthenticationProvider;
    }

    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        List<AuthenticationProvider> authenticationProviderList = new ArrayList<AuthenticationProvider>();
        authenticationProviderList.add(customAuthenticationProvider());
        AuthenticationManager authenticationManager = new ProviderManager(
                authenticationProviderList);
        return authenticationManager;
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests().antMatchers("/reportspage").hasRole("REPORT")
                .antMatchers("/rawdatapage").hasRole("RAWDATA").anyRequest()
                .hasRole("USER").and().formLogin().loginPage("/login")
                .failureUrl("/login?error")
                .loginProcessingUrl("/j_spring_security_check")
                .passwordParameter("j_password")
                .usernameParameter("j_username").defaultSuccessUrl("/")
                .permitAll().and().httpBasic().and().logout()
                .logoutSuccessUrl("/login?logout").and().csrf().disable()
                .addFilter(customUsernamePasswordAuthenticationFilter());
    }

}

It is working fine on my application. you can download this entire project from url: https://github.com/programsji/rohit/tree/master/UsernamePasswordAuthenticationFilter

Solution 3

Try to add @Component to your MyUsernamePasswordAuthenticationFilter class.

This annotation makes the class considered as candidates for auto-detection, see: @Component


For this:

<custom-filter position="FORM_LOGIN_FILTER" ref="myFilter"/>

You can add this:

.addFilter[Before|After](authenticationTokenProcessingFilter, UsernamePasswordAuthenticationFilter.class)

See: Standard Filter Aliases and Ordering

Share:
49,126
Admin
Author by

Admin

Updated on July 09, 2022

Comments

  • Admin
    Admin almost 2 years

    What is the equivalent Java configuration for the Spring Security <custom-filter> tag?

    <http>
      <custom-filter position="FORM_LOGIN_FILTER" ref="myFilter"/>
    </http>
    

    I tried

    http.addFilter( new MyUsernamePasswordAuthenticationFilter() )
    

    where the class extends the default filter, but it always employs the formLogin default.

    My filter:

    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    import org.springframework.security.authentication.AuthenticationServiceException;
    import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
    import org.springframework.security.core.Authentication; 
    import org.springframework.security.core.AuthenticationException;
    import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
    
    public class MyUsernamePasswordAuthenticationFilter extends UsernamePasswordAuthenticationFilter{
    
        // proof of concept of how the http.addFilter() works
    
        @Override
        public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
                throws AuthenticationException {
            if (!request.getMethod().equals("POST")) {
                throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
            }
    
            System.out.println("running my own version of UsernmePasswordFilter ... ");
    
            String username = obtainUsername(request);
            String password = obtainPassword(request);
    
            if (username == null) {
                username = "";
            }
    
            if (password == null) {
                password = "";
            }
    
            username = username.trim();
    
            UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);
    
            // Allow subclasses to set the "details" property
            setDetails(request, authRequest);
    
            return this.getAuthenticationManager().authenticate(authRequest);
        }
    }
    

    The relevant configuration piece:

    @Configuration
    @EnableWebMvcSecurity  // annotate class configuring AuthenticationManagerBuilder
    @ComponentScan("com.kayjed")
    public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
    
        @Override
        protected void configure(HttpSecurity http) throws Exception {
    
          http
            .authorizeRequests()
                .antMatchers("/resources/**","/signup").permitAll()
                .anyRequest().authenticated()
                .and()
            .formLogin()
                .loginPage("/login")
                .permitAll()
                .and()
            .logout()
                .permitAll();
    
          http.addFilter(new MyUsernamePasswordAuthenticationFilter());
        }
    
        ...
    }
    

    Running the MVC app in the debugger always shows the login attempts authentication from the default UsernamePasswordAuthenticationFilter instead of my intention of employing the MyUsernamePasswordAuthenticationFilter class.

    Anyways, I am not trying to get someone to debug code; rather, I would love to see a good example using Java configuration that performs the equivalent of the custom-filter element in the XML approach. The documentation is a bit terse.

  • Clockwork-Muse
    Clockwork-Muse over 9 years
    Perhaps you could explain why you think this solves the problem?
  • Charles Morin
    Charles Morin over 9 years
    Did you find any solution to your problem? I am looking for the exact same thing. Thank you for sharing your solution, if you have any.
  • Altair7852
    Altair7852 about 9 years
    You can also get default AuthenticationManager instance injected through overriden setAuthenticationManager() setter of MyAuthenticationFilter.
  • Squall
    Squall almost 9 years
    The link you've provided is offline.
  • noodles
    noodles over 8 years
    @Altair7852 Could you share a project about MyAuthenticationFilter
  • PurrgnarMeowbro
    PurrgnarMeowbro over 7 years
    @noodles, MyAuthenticationFilter is his implementation of UsernamePasswordAuthenticationFilter.