Spring mvc interceptor example: force user to change onetime password
March 30, 2012 10:07:25 Last update: March 08, 2013 13:41:57
After a user resets a password, I want to force the user to change the password before she gets access to secured content. This is usually done with a servlet filter. But with Spring MVC, you can also use a HandlerInterceptor. According to Spring JavaDoc:
HandlerInterceptor is basically similar to a Servlet 2.3 Filter, but in contrast to the latter it just allows custom pre-processing with the option of prohibiting the execution of the handler itself, and custom post-processing. Filters are more powerful, for example they allow for exchanging the request and response objects that are handed down the chain. Note that a filter gets configured in web.xml, a HandlerInterceptor in the application context.
As a basic guideline, fine-grained handler-related preprocessing tasks are candidates for HandlerInterceptor implementations, especially factored-out common handler code and authorization checks. On the other hand, a Filter is well-suited for request content and view content handling, like multipart forms and GZIP compression. This typically shows when one needs to map the filter to certain content types (e.g. images), or to all requests.
This is how to make it work:
The interceptor may also be inserted for a specific HandlerMapping:
Important Update: the above example code only works for Spring releases prior to 3.1. Since 3.1, the
HandlerInterceptor is basically similar to a Servlet 2.3 Filter, but in contrast to the latter it just allows custom pre-processing with the option of prohibiting the execution of the handler itself, and custom post-processing. Filters are more powerful, for example they allow for exchanging the request and response objects that are handed down the chain. Note that a filter gets configured in web.xml, a HandlerInterceptor in the application context.
As a basic guideline, fine-grained handler-related preprocessing tasks are candidates for HandlerInterceptor implementations, especially factored-out common handler code and authorization checks. On the other hand, a Filter is well-suited for request content and view content handling, like multipart forms and GZIP compression. This typically shows when one needs to map the filter to certain content types (e.g. images), or to all requests.
This is how to make it work:
- Code the interceptor:
import javax.servlet.ServletException; import javax.servlet.http.HttpSession; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.web.servlet.handler.HandlerInterceptorAdapter; /** * Redirect user to change onetime password. */ public class ResetPasswordInterceptor extends HandlerInterceptorAdapter { public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws ServletException { if (handler instanceof MyController) { // only intercept my controller, don't mess with other controllers. if (isOnetimePassword()) { if (!request.getPathInfo().equals("/changepassword")) { // avoid redirection infinite loop! redirect(request, response, "/changepassword"); return false; // request handled, no need to bother controller } } } return true; } private void redirect(HttpServletRequest request, HttpServletResponse response, String path) throws ServletException { try { response.sendRedirect(request.getContextPath() + path); } catch (java.io.IOException e) { throw new ServletException(e); } } private boolean isOnetimePassword() { return true; } }
- Insert the interceptor in Spring config:
<!-- register "global" interceptor beans to apply to all registered HandlerMappings --> <mvc:interceptors> <bean class="com.example.ResetPasswordInterceptor"/> </mvc:interceptors>
The interceptor may also be inserted for a specific HandlerMapping:
<bean id="handlerMapping" class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"> <property name="interceptors"> <bean class="com.example.ResetPasswordInterceptor"/> </property> </bean>
Important Update: the above example code only works for Spring releases prior to 3.1. Since 3.1, the
handler parameter in preHandle no longer refers to the Controller class. Instead, it is an instance of org.springframework.web.method.HandlerMethod which identifies the specific controller method that will be invoked.