Last Updated on 30 October 2020   |   Print Email
This Spring Security article will guide you how to intercept the authentication process of Spring Security in order to run custom logics just before the authentication takes place. In practice, we need to do the following tasks before authentication:
Check the spam score (using Google ReCaptcha API) of the current login request to decide whether to require OTP (One-Time Password) or not.
Clear failed login attempts if the lock already expired.
Any custom logics that need to be executed just before authentication.
The following diagram helps you understand the workflow under the context of Spring Security’s authentication process:As you can see, it requires to setup a custom filter that is executed before Spring Security filter. This custom filter will override all the existing configurations for login success handler, login failure handler and logout success handler. That means when you configure a before authentication filter, you need to configure those handlers in this filter (if needed).
1. Simple Before Authentication Filter Configuration
Suppose that you have an existing Spring Boot project with the login function already implemented. And now you want to configure a before authentication filter.First, you need to create a new class that extends the UsernamePasswordAuthenticationFilter class as follows:
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
public class CustomBeforeAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
public CustomBeforeAuthenticationFilter() {
setUsernameParameter("email");
super.setRequiresAuthenticationRequestMatcher(new AntPathRequestMatcher("/login", "POST"));
}
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
throws AuthenticationException {
String email = request.getParameter("email");
System.out.println("The user " + email + " is about to login");
// run custom logics...
return super.attemptAuthentication(request, response);
}
}
You see, the code in the constructor sets username parameter to “email” because the custom login page uses “email” as name of the username field; and it this filter is configured to be invoked only for the request /login with HTTP POST method.And the callback method is attemptAuthentication() which will be executed right before Spring Security authenticates the user – this method is where you put the custom logics. And finally it should call super.attemptAuthentication() to delegate processing to the Spring Security filter.
In this example, it just prints the email of the user who is about to login. And configure this filter in the Spring security configuration class as follows:
As you can see, a custom before authentication filter is configured in the getBeforeAuthenticationFilter() method. And it is required to set the authentication manager and authentication failure handler for this filter (if not, failed login attempt will cause an error). The addFilterBefore() method of the HttpSecurity class will register the custom filter before Spring security filter.
2. Advanced Before Authentication Filter Configuration
In case the before authentication filter needs to depend on a business/service class to perform the custom logics, you need to configure the filter class as follows:
@Component
public class CustomBeforeAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
@Autowired
private CustomerServices customerService;
@Autowired
public void setAuthenticationManager(AuthenticationManager authenticationManager) {
super.setAuthenticationManager(authenticationManager);
}
@Autowired
@Override
public void setAuthenticationFailureHandler(AuthenticationFailureHandler failureHandler) {
super.setAuthenticationFailureHandler(failureHandler);
}
@Autowired
@Override
public void setAuthenticationSuccessHandler(AuthenticationSuccessHandler successHandler) {
super.setAuthenticationSuccessHandler(successHandler);
}
public CustomBeforeAuthenticationFilter() {
setUsernameParameter("email");
super.setRequiresAuthenticationRequestMatcher(new AntPathRequestMatcher("/login", "POST"));
}
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
throws AuthenticationException {
String email = request.getParameter("email");
Customer customer = customerService.getCustomerByEmail(email);
// performs custom logics on customer...
return super.attemptAuthentication(request, response);
}
}
Here, you can see the filter requires an instance of the CustomerService class, which will be injected by Spring framework as @Autowired is used. And we also configure @Autowired for the authentication manager, authentication success handler and authentication failure handler.Then you need to update the Spring Security configuration class like this:
Here, note the declaration of an authentication manager bean, which will be injected to the filter instance. We also need to create an authentication success handler class:
@Component
public class CustomLoginSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler {
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
Authentication authentication) throws ServletException, IOException {
// performs custom logics on successful login
super.onAuthenticationSuccess(request, response, authentication);
}
}
And authentication failure handler class:
@Component
public class CustomLoginFailureHandler extends SimpleUrlAuthenticationFailureHandler {
@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
AuthenticationException exception) throws IOException, ServletException {
// peforms custom logics on failed login attempt
super.onAuthenticationFailure(request, response, exception);
}
}
That’s how to configure a before authentication filter in a Spring Boot application. To see the coding in action, I recommend you to watch the following video:
Nam Ha Minh is certified Java programmer (SCJP and SCWCD). He began programming with Java back in the days of Java 1.4 and has been passionate about it ever since. You can connect with him on Facebook and watch his Java videos on YouTube.
Comments
equivalent in xml