5

I'm extending UsernamePasswordAuthenticationFilter so that I can add custom field to save them into the session.

public class AuthFilter extends UsernamePasswordAuthenticationFilter {

@Override
public Authentication attemptAuthentication(HttpServletRequest request,
        HttpServletResponse response) throws AuthenticationException {
    //String dbValue = request.getParameter("dbParam");
    //request.getSession().setAttribute("dbValue", dbValue);
    System.out.println("attempting to authentificate");
    while (request.getAttributeNames().hasMoreElements()) {
        String e = (String) request.getAttributeNames().nextElement();
        System.out.println("param name : " + e + " and param value : " + request.getAttribute(e));
    }

    return super.attemptAuthentication(request, response);
    }
}

And my WebSecurityConfig

@Configuration
@EnableWebMvcSecurity
@Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

@Autowired
private UserDetailsService userDetailsService;


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

@Override
protected void configure(HttpSecurity http) throws Exception {
    http.addFilterAfter(customUsernamePasswordAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);

    http.exceptionHandling().accessDeniedPage("/403").and()
            .authorizeRequests().antMatchers("/login", "/public/**").permitAll()
            .antMatchers("/users/**").hasAuthority("ADMIN")
            .anyRequest()
            .authenticated().and().formLogin().loginPage("/login")
            .defaultSuccessUrl("/index").permitAll().and().logout()
            .permitAll();


    http.sessionManagement().maximumSessions(1)
            .expiredUrl("/login?expired").and()
            .sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
            .invalidSessionUrl("/");
}

@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws  Exception {
    auth.eraseCredentials(false)
    .userDetailsService(userDetailsService);
}

Mapping filter: 'customUsernamePasswordAuthenticationFilter' to: [/*]

So I know for sure that the filter is correctly added, but I can never print out what's inside, so it's not called during authentification.

I use Thymeleaf and no xml configuration.

as @M. Deinum suggested, i changed my UsernamePasswordAuthenticationFilter, to AbstractAuthenticationProcessingFilter, called super(new AntPathRequestMatcher("/login","POST")); Changed addFilterAfter to addFilterBefore, and a bit of code, and it worked !

11
  • Well no because authentication already happend so there is nothing to do anymore. Add it before instead of after the UsernamePasswordAuthenticationFilter. Commented May 17, 2015 at 20:04
  • Tried it, same issue. Commented May 17, 2015 at 20:13
  • You also might want to avoid registering it as a @Bean as it now is also added to the normal filter chain instead of the spring security chain. Spring Boot by default registers all Filter defined @Bean as filters, you only want to add them to the Spring Security filter chain. Commented May 17, 2015 at 20:32
  • I got rid of the Bean annotation, it is registered, but still not called during the authentification since i don't see my prints. Commented May 17, 2015 at 20:37
  • 1
    Well actually no it doesn't, it gets the version that is managed by Spring Boot. So it depends on the Spring Boot version. Assuming you are using the latest Spring Boot (1.2.3) you are probably using Spring Security 3.2.7 this version maps the UsernamePasswordAuthenticationFilter to /j_spring_security_check however when using java config this is changed to /login for the default one. However yours is still mapped to the old URL. Create a default constructor which calls super(new AntPathRequestMatcher("/login","POST"));. Commented May 17, 2015 at 20:49

2 Answers 2

11

Assuming you are using the latest Spring Boot (1.2.3) you are using Spring Security 3.2.7 This version maps the UsernamePasswordAuthenticationFilter to /j_spring_security_check. However when using java based configuration this is changed to /login.

Yours is still mapped to the old URL. To fix this extend AbstractAuthenticationProcessingFilter add a default no-args constructor which calls the super constructor which takes a RequestMatcher. Drawback of this is that if you still require (or want to extend) the functionality of the UsernamePasswordAuthenticationFilter you would have to duplicate it.

public AuthFilter() {
    super(new AntPathRequestMatcher("/login","POST"));
}

Another solution is to still extend the UsernamePasswordAuthenticationFilter and call setRequiresAuthenticationRequestMatcher from there.

public AuthFilter() {
    super();
    setRequiresAuthenticationRequestMatcher(new AntPathRequestMatcher("/login","POST"));
}

Or you call that method from your factory method.

@Bean
public AuthFilter customUsernamePasswordAuthenticationFilter()
    throws Exception {
    AuthFilter customUsernamePasswordAuthenticationFilter = new AuthFilter();
    customUsernamePasswordAuthenticationFilter
        .setAuthenticationManager(authenticationManagerBean());
    customUsernamePasswordAuthenticationFilter
        .setRequiresAuthenticationRequestMatcher(new AntPathRequestMatcher("/login","POST"));
    return customUsernamePasswordAuthenticationFilter;
}

There're is also another problem with your configuration, your filter will never be executed because it is executed after the default UsernamePasswordAuthenticationFilter and authentication already happened your filter will never execute. Make sure it executes before the default filter.

@Override
protected void configure(HttpSecurity http) throws Exception {
    http.addFilterBefore(customUsernamePasswordAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
    ...
}
Sign up to request clarification or add additional context in comments.

2 Comments

UsernamePasswordAuthenticationFilter doesn't have a super with arguments, that's why i extended another class
Realized that later. Problem is that your filter now doesn't have the login functionality as that was specific to the UsernamePasswordAuthenticationFilter. I modified the answer to include 2 other possible solutions...
1

In order to make your custom UsernamePasswordAuthenticationFilter implementation work, add .loginProcessingUrl("/dologin") to HttpSecurity in your WebSecurityConfig, here "/dologin" is action attribute value of the html form element:

@Override
//@Order(Ordered.HIGHEST_PRECEDENCE)
public void configure(HttpSecurity http) throws Exception { // @formatter:off
    http
        ...
        ...
        .formLogin().loginPage("/login")
   -->  .loginProcessingUrl("/dologin") <-- add here
        ...
   -->  .addFilterBefore(new AuthFilter(authenticationManagerBean()),UsernamePasswordAuthenticationFilter.class)
 }

Next is to provide custom UsernamePasswordAuthenticationFilter implementation:

public class AuthFilter extends UsernamePasswordAuthenticationFilter {

   AuthenticationManager authenticationManager;

   private boolean continueChainBeforeSuccessfulAuthentication = false;


   public AuthFilter( AuthenticationManager authenticationManager){
      this.authenticationManager = authenticationManager;
      //idk why I have to do this, otherwise it's null
      super.setAuthenticationManager(authenticationManager);
   }

    public AuthFilter() {}

   private SessionAuthenticationStrategy sessionStrategy = new NullAuthenticatedSessionStrategy();
   //path to which this filter will intercept
   RequestMatcher customFilterUrl = new AntPathRequestMatcher("/dologin"); <--

  //dofilter method is copied from AbstractAuthenticationProcessingFilter
  @Override
  public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
      HttpServletRequest request = (HttpServletRequest)req;
      HttpServletResponse response = (HttpServletResponse)res;
      //if no match then go to next filter
      if (!customFilterUrl.matches(request)) {
         chain.doFilter(request, response);
      } else {

        Authentication authResult;
        try {
            authResult = this.attemptAuthentication(request, response);
            if (authResult == null) {
                return;
            }

            this.sessionStrategy.onAuthentication(authResult, request, response);
        } catch (InternalAuthenticationServiceException var8) {
            this.logger.error("An internal error occurred while trying to authenticate the user.", var8);
            this.unsuccessfulAuthentication(request, response, var8);
            return;
        } catch (AuthenticationException var9) {
            this.unsuccessfulAuthentication(request, response, var9);
            return;
        }

        if (this.continueChainBeforeSuccessfulAuthentication) {
            chain.doFilter(request, response);
        }

        successfulAuthentication(request, response, chain, authResult);
    }
}

@Override
public Authentication attemptAuthentication(HttpServletRequest request, 
 HttpServletResponse response)
        throws AuthenticationException {

    System.out.println("Your prints"); <--

    return super.attemptAuthentication(request,response);
  }
}

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.