原文链接:http://lixuanbin.iteye.com/blog/2250100
1. 需求描述
某内部管理系统采用Spring MVC搭建,用户可以登录系统进行CRUD以及其他的一些日常管理操作。现在希望对某些敏感操作(譬如修改重要数据)进行拦截,在日志中记录下该操作的执行人,操作时间,操作名称,传入参数以及返回结果等信息,以便万一出问题后追究查验。
2. 解决思路
有两种方案:
(1)把对应的MVC方法使用AOP去拦截,这个是方法级别的拦截;
(2)使用Spring MVC Interceptor去拦截,这个是基于URL的拦截。
那么,如何选择呢?考虑到我们这个是WEB项目,只有登录用户才能执行管理操作,而登录用户的用户信息一般放在Session里,使用基于URL拦截的方法便于提取Session数据;而且,被Spring MVC @Controller注解的类,都被AnnotationMethodHandlerAdapter给代理了,使用AOP进行拦截的话需要做些特殊处理。所以,在这里我们选择使用Interceptor拦截。
3. 具体实现
(1)Spring配置文件
在beans头部标签加入:
...
xmlns:mvc="http://www.springframework.org/schema/mvc"
...
xsi:schemaLocation="...
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
..."
然后加上mvc:interceptors拦截声明:
1 <mvc:interceptors> 2 <mvc:interceptor> 3 <mvc:mapping path="/**"/><!--拦截所有controller--> 4 <mvc:exclude-mapping path="/js/**" /> 5 <mvc:exclude-mapping path="/css/**" /> 6 <mvc:exclude-mapping path="/img/**" /> 7 <mvc:exclude-mapping path="*.woff" /> 8 <mvc:exclude-mapping path="*.ico" /> 9 <mvc:exclude-mapping path="*.swf" /> 10 <mvc:exclude-mapping path="*.png" /> 11 <bean id="log4UpdateOrDeleteInterceptor" 12 class="com.XXX.testmvc.interceptor.Log4UpdateOrDeleteInterceptor"></bean> 13 </mvc:interceptor> 14 </mvc:interceptors>
The mapping matches URLs using the following rules:
- ? matches one character
- * matches zero or more characters
- ** matches zero or more 'directories' in a path
Some examples:
com/t?st.jsp
- matchescom/test.jsp
but alsocom/tast.jsp
orcom/txst.jsp
com/*.jsp
- matches all.jsp
files in thecom
directorycom/**/test.jsp
- matches alltest.jsp
files underneath thecom
pathorg/springframework/**/*.jsp
- matches all.jsp
files underneath theorg/springframework
pathorg/**/servlet/bla.jsp
- matchesorg/springframework/servlet/bla.jsp
but alsoorg/springframework/testing/servlet/bla.jsp
andorg/servlet/bla.jsp
(2)实现拦截器
继承HandlerInterceptorAdapter并按需覆写里面的preHandle和postHandle方法即可。
1 /** 2 * 拦截敏感操作并记录到日志 3 * 4 * @author 5 */ 6 public class Log4UpdateOrDeleteInterceptor extends HandlerInterceptorAdapter { 7 protected static final Logger log = Logger.getLogger(Log4UpdateOrDeleteInterceptor.class); 8 9 public boolean preHandle(HttpServletRequest request, HttpServletResponse response, 10 Object handler) throws Exception { 11 HttpSession session = request.getSession(); 12 String requestUri = request.getRequestURI(); 13 log.debug("request uri:" + requestUri); 14 String contextPath = request.getContextPath(); 15 String url = requestUri.substring(contextPath.length()); 16 if (StringUtils.contains(url, "add") || StringUtils.contains(url, "edit") 17 || StringUtils.contains(url, "delete")) { 18 String user = session.getAttribute(SessionKey.USERNAME_SESSION_NAME) != null ? (String) session 19 .getAttribute(SessionKey.USERNAME_SESSION_NAME) : null; 20 StringBuffer sb = new StringBuffer(); 21 Enumeration<String> a = null; 22 a = request.getParameterNames(); 23 while (a.hasMoreElements()) { 24 String key = a.nextElement(); 25 sb.append(key + ":" + request.getParameter(key) + ", "); 26 } 27 log.warn(String.format("FBI request warning! user: %s, url: %s, params: {%s}", user, 28 url, StringUtils.removeEnd(StringUtils.trim(sb.toString()), ","))); 29 } 30 return true; 31 } 32 33 public void postHandle(HttpServletRequest request, HttpServletResponse response, Object o, 34 ModelAndView mav) throws Exception { 35 HttpSession session = request.getSession(); 36 String requestUri = request.getRequestURI(); 37 log.debug("request uri:" + requestUri); 38 String contextPath = request.getContextPath(); 39 String url = requestUri.substring(contextPath.length()); 40 if ((StringUtils.contains(url, "add") || StringUtils.contains(url, "edit") || StringUtils 41 .contains(url, "delete")) && mav != null) { 42 String user = session.getAttribute(SessionKey.USERNAME_SESSION_NAME) != null ? (String) session 43 .getAttribute(SessionKey.USERNAME_SESSION_NAME) : null; 44 Map<String, Object> map = mav.getModel(); 45 StringBuffer sb = new StringBuffer(); 46 for (Map.Entry<String, Object> entry : map.entrySet()) { 47 sb.append(entry.getKey() + ":" 48 + (entry.getValue() != null ? entry.getValue().toString() : null) + ", "); 49 } 50 log.warn(String.format("FBI response warning! user: %s, url: %s, models: {%s}", user, 51 url, StringUtils.removeEnd(StringUtils.trim(sb.toString()), ","))); 52 } 53 } 54 55 public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object o, 56 Exception excptn) throws Exception { 57 // System.out.println("afterCompletion"); 58 } 59 60 }
本例检查URI是否含有敏感操作来判断是否需要进行日志记录,如果您采用了REST接口,也可以加上request method的校验。