Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SpringBoot UsernamePasswordAuthenticationFilter issue

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 !

like image 780
Santo Avatar asked May 17 '15 13:05

Santo


2 Answers

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);
    ...
}
like image 159
M. Deinum Avatar answered Nov 03 '22 03:11

M. Deinum


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);
  }
}
like image 22
S.Step Avatar answered Nov 03 '22 02:11

S.Step