The Master Class of "Learn Spring Security" is out:
1. Introduction
In this tutorial we are going to focus on the Spring MVC HandlerInterceptor. More specifically, we will change Spring MVC’s model parameters before and after handling a request.
If you want to read about HandlerInterceptor’s basics, check out this article.
2. Maven Dependencies
In order to use Interceptors, you need to include the following section in a dependencies section of your pom.xml file:
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> <version>4.3.2.RELEASE</version> </dependency>
Latest version can be found here.
This dependency only covers Spring Web so don’t forget to add spring-core and spring-context for a full web application, and a logging library of your choice.
3. Custom Implementation
One of the use cases of HandlerInterceptor is adding common/user specific parameters to a model, which will be available on each generated view.
In our example, we will use custom interceptor implementation to add logged user’s username to model parameters. In more complex systems we may add more specific information like: user avatar path, user location, etc.
Let’s start with defining our new Interceptor class:
public class UserInterceptor extends HandlerInterceptorAdapter { private static Logger log = LoggerFactory.getLogger(UserInterceptor.class); ... }
We extend HandlerInterceptorAdapter, as we only want to implement preHandle() and postHandle() methods.
As we mentioned before, we want to add logged user’s name to a model. First of all, we need to check if a user is logged in. We may obtain this information by checking SecurityContextHolder:
public static boolean isUserLogged() { try { return !SecurityContextHolder.getContext().getAuthentication() .getName().equals("anonymousUser"); } catch (Exception e) { return false; } }
When an HttpSession is established, but nobody is logged in, a username in Spring Security context equals to anonymousUser. Next, we proceed with implementation of preHandle():
3.1. Method preHandle()
Before handling a request, we cannot access model parameters. In order to add username, we need to use HttpSession to set parameters:
@Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object object) throws Exception { if (isUserLogged()) { addToModelUserDetails(request.getSession()); } return true; }
This is crucial if we are using some of this information before handling a request. As we see, we are checking if a user is logged in and then add parameters to our request by obtaining its session:
private void addToModelUserDetails(HttpSession session) { log.info("=============== addToModelUserDetails ========================="); String loggedUsername = SecurityContextHolder.getContext().getAuthentication().getName(); session.setAttribute("username", loggedUsername); log.info("user(" + loggedUsername + ") session : " + session); log.info("=============== addToModelUserDetails ========================="); }
We used SecurityContextHolder to obtain loggedUsername. You may override Spring Security UserDetails implementation to obtain email instead of a standard username.
3.2. Method postHandle()
After handling a request, our model parameters are available, so we may access them to change values or add new ones. In order to do that, we use the overridden postHandle() method:
@Override public void postHandle( HttpServletRequest req, HttpServletResponse res, Object o, ModelAndView model) throws Exception { if (model != null && !isRedirectView(model)) { if (isUserLogged()) { addToModelUserDetails(model); } } }
Let’s take a look at the implementation details.
First of all, it’s better to check if the model is not null. It will prevent us from encountering a NullPointerException.
Moreover, we may check if a View is not an instance of RedirectView.
There is no need to add/change parameters after request is handled and then redirected, as immediately, new controller will perform handling again. To check if the view is redirected, we are introducing the following method:
public static boolean isRedirectView(ModelAndView mv) { String viewName = mv.getViewName(); if (viewName.startsWith("redirect:/")) { return true; } View view = mv.getView(); return (view != null && view instanceof SmartView && ((SmartView) view).isRedirectView()); }
Finally, we are checking again if a user is logged, and if yes, we are adding parameters to Spring model:
private void addToModelUserDetails(ModelAndView model) { log.info("=============== addToModelUserDetails ========================="); String loggedUsername = SecurityContextHolder.getContext() .getAuthentication().getName(); model.addObject("loggedUsername", loggedUsername); log.trace("session : " + model.getModel()); log.info("=============== addToModelUserDetails ========================="); }
Please note that logging is very important, as this logic works “behind the scenes” of our application. It is easy to forget that we are changing some model parameters on each View without logging it properly.
4. Configuration
To add our newly created Interceptor into Spring configuration, we need to override addInterceptors() method inside WebConfig class that extends WebMvcConfigurerAdapter:
@Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new UserInterceptor()); }
We may achieve the same configuration by editing our XML Spring configuration file:
<mvc:interceptors> <bean id="userInterceptor" class="org.baeldung.web.interceptor.UserInterceptor"/> </mvc:interceptors>
From this moment, we may access all user-related parameters on all generated views.
Please notice, if multiple Spring Interceptors are configured, the preHandle() method is executed in the order of configuration whereas postHandle() and afterCompletion() methods are invoked in the reverse order.
5. Conclusion
This tutorial presents intercepting web requests using Spring MVC’s HandlerInterceptor in order to provide user information.
In this particular example, we focused on adding logged user’s details in our web application to a model parameters. You may extend this HandlerInterceptor implementation by adding more detailed information.
All examples and configurations are available here on GitHub.
5.1. Articles in the Series
All articles of the series:
- Introduction to Spring MVC Handler Interceptors
- Changing Spring Model Parameters with Handler Interceptor (this one)