一、首先说一下 接口类 HandlerInterceptor
public interface HandlerInterceptor
Workflow interface that allows for customized handler execution chains. Applications can register any number of existing or custom interceptors for certain groups of handlers, to add common preprocessing behavior without needing to modify each handler implementation.
我们可以根据这个接口自定义任意数目的handler的相关类。并且拦截器有先后顺序,根据{dispatchservlet定义的servletmapping 属性下面的 servletname}-servlet.xml.
A HandlerInterceptor gets called before the appropriate HandlerAdapter triggers the execution of the handler itself. This mechanism can be used for a large field of preprocessing aspects, e.g. for authorization checks, or common handler behavior like locale or theme changes. Its main purpose is to allow for factoring out repetitive handler code.
handlerinterceptor 里面的prehandle会预先执行,然后在到posthandle和aftercompletion。
In an async processing scenario, the handler may be executed in a separate thread while the main thread exits without rendering or invoking the postHandle
and afterCompletion
callbacks. When concurrent handler execution completes, the request is dispatched back in order to proceed with rendering the model and all methods of this contract are invoked again. For further options and details see org.springframework.web.servlet.AsyncHandlerInterceptor
在异步处理的情况下,handlerinterceptor怎么来处理。
Typically an interceptor chain is defined per HandlerMapping bean, sharing its granularity. To be able to apply a certain interceptor chain to a group of handlers, one needs to map the desired handlers via one HandlerMapping bean. The interceptors themselves are defined as beans in the application context, referenced by the mapping bean definition via its "interceptors" property (in XML: a <list> of <ref>).
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.
Interceptors can be configured using the interceptors property, which is present on all HandlerMapping classes extending fromAbstractHandlerMapping. This is shown in the example below:
二、微信企业号拦截器实例
package me.chanjar.weixin.cp.Interceptor;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import me.chanjar.weixin.cp.api.Writelog;
import org.springframework.mobile.device.Device;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
public class MyHandlerInterceptor extends HandlerInterceptorAdapter implements
HandlerInterceptor {
private int openingTime; //需要定义get和set方法,用于在配置文件里面初始化
private int closingTime;
public void setOpeningTime(int openingTime) {
this.openingTime = openingTime;
}
public void setClosingTime(int closingTime) {
this.closingTime = closingTime;
}
private String LoginIGNORE_URI;// "/login.do";// 忽略的网址
private String messagesend;// "/login.do";// 忽略的网址
public String getMessagesend() {
return messagesend;
}
public void setMessagesend(String messagesend) {
this.messagesend = messagesend;
}
public String getLoginIGNORE_URI() {
return LoginIGNORE_URI;
}
public void setLoginIGNORE_URI(String loginIGNORE_URI) {
LoginIGNORE_URI = loginIGNORE_URI;
}
public String getLegalip() {
return legalip;
}
public void setLegalip(String legalip) {
this.legalip = legalip;
}
private String legalip;// ="****";//内网防火墙的地址。
private String loginoutURL;
public String getLoginoutURL() {
return loginoutURL;
}
public void setLoginoutURL(String loginoutURL) {
this.loginoutURL = loginoutURL;
}
@Override
/**
* 该方法也是需要当前对应的Interceptor的preHandle方法的返回值为true时才会执行。该方法将在整个请求完成之后,也就是DispatcherServlet渲染了视图执行,
* 这个方法的主要作用是用于清理资源的。
*/
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response, Object handler, Exception ex)
throws Exception {
Writelog.writetolog("intercepter_aftercompletion.");
}
@Override
/**
* 这个方法只会在当前这个Interceptor的preHandle方法返回值为true的时候才会执行。postHandle是进行处理器拦截用的,它的执行时间是在处理器进行处理之后,也就是在Controller的方法调用之后执行,但是它会在DispatcherServlet进行视图的渲染之前执行,也就是说在这个方法中你可以对ModelAndView进行操作。这个方法的链式结构跟正常访问的方向是相反的,也就是说先声明的Interceptor拦截器该方法反而会后调用,这跟Struts2里面的拦截器的执行过程有点像,
* 只是Struts2里面的intercept方法中要手动的调用ActionInvocation的invoke方法,Struts2中调用ActionInvocation的invoke方法就是调用下一个Interceptor
* 或者是调用action,然后要在Interceptor之前调用的内容都写在调用invoke之前,要在Interceptor之后调用的内容都写在调用invoke方法之后。
*/
public void postHandle(HttpServletRequest request,
HttpServletResponse response, Object arg2, ModelAndView arg3)
throws Exception {
}
@Override
/**preHandle方法是进行处理器拦截用的,顾名思义,
**该方法将在Controller处理之前进行调用**
**当它返回为false 时,表示请求结束,请求不会往后传递*
*SpringMVC中的Interceptor拦截器是链式的,可以同时存在
*多个Interceptor,然后SpringMVC会根据声明的前后顺序一个接一个的执行,
***而且所有的Interceptor中的preHandle方法都会在
*Controller方法调用之前调用。SpringMVC的这种Interceptor链式结构也是可以进行中断的,
*这种中断方式是令preHandle的返 回值为false,当preHandle的返回值为false的时候整个请求就结束了。
**/
// 参数说明 预处理回调方法,实现处理器的预处理(如登录检查),第三个参数为响应的处理器(如我们上一章的Controller实现
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
Writelog.writetolog("intercepter_prehandle.");
Device device = (Device) request.getAttribute("currentDevice");
Writelog.writetolog("判断客户端类型:" + device.isMobile());
String requestURlString = request.getRequestURI();
String ipaddress = request.getRemoteAddr();// 获得客户端的ip地址,10.116.200.2是合法的内网防火墙地址
Writelog.writetolog("客户端ip:" + ipaddress);
Writelog.writetolog("获得请求的URL:" + requestURlString);
Calendar cal = Calendar.getInstance();
SimpleDateFormat sdf = new SimpleDateFormat("dd");
Date date = (Date) cal.getTime();
int nowdate = Integer.parseInt(sdf.format(date));
if (requestURlString.contains(LoginIGNORE_URI)) {// 如果请求的网址在不拦截数组里面
if (ipaddress.equals(legalip)) {// 增加IP地址过滤,如果获得的请求ip是10.116.2.200
Writelog.writetolog("包含login.do并且是legalip");
return true;// 准许访问
}
}
// 如果是Loginout
if (requestURlString.contains(loginoutURL)) {// 如果请求的网址在不拦截数组里面
if (ipaddress.equals(legalip)) {// 增加IP地址过滤,如果获得的请求ip是*****
Writelog.writetolog("包含login.do并且是legalip");
return true;// 准许访问
}
}
// 微信发送消息不拦截
if (requestURlString.contains(messagesend)) {// 如果请求的网址在不拦截数组里面
Writelog.writetolog("微信发送消息,包含justdoit.do");
return true;// 准许访问
}
// 用于查询工资规则用,固定开放时间
if (openingTime <= nowdate && nowdate <= closingTime) {// 判断是否在合法时间
if (ipaddress.equals(legalip) && device.isMobile()) {// 增加IP地址过滤,如果获得的请求ip是10.116.2.200
Writelog.writetolog("是合法时间!");
return true;// 准许访问
}
}
response.sendRedirect("/Error.jsp");
return false;
}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">
<!-- DispatcherServlet Context: defines this servlet's request-processing infrastructure -->
<!--注解驱动 Enables the Spring MVC @Controller programming model -->
<mvc:annotation-driven>
<!--<mvc:argument-resolvers>
<bean class="org.springframework.mobile.device.DeviceWebArgumentResolver" />
</mvc:argument-resolvers>-->
</mvc:annotation-driven>
<!-- 扫描器 -->
<context:component-scan base-package="me" />
<!---配置拦截器不拦截的静态资源
<mvc:resources mapping="/img/**" location="/img/" />
<mvc:resources mapping="/fonts/**" location="/fonts/" />
<mvc:resources mapping="/js/**" location="/js/" />
<mvc:resources mapping="/css/**" location="/css/" />-->
<!--添加spring mvc intercepters
<mvc:interceptors>
<bean class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor" />
<mvc:interceptor>
<mvc:mapping path="/"/>
<bean class="me.chanjar.weixin.cp.Interceptor.MyHandlerInterceptor">
<property name="openingTime" value="15"/>
<property name="closingTime" value="26"/>
</bean>
</mvc:interceptor>
</mvc:interceptors>-->
<mvc:interceptors>
<bean class="org.springframework.mobile.device.DeviceResolverHandlerInterceptor" />
<bean class="me.chanjar.weixin.cp.Interceptor.MyHandlerInterceptor">
<property name="openingTime" value="1"/>
<property name="closingTime" value="9"/>
<property name="LoginIGNORE_URI" value="/login.do"/><!--用于定义不拦截的地址-->
<property name="legalip" value="********"/><!--定义防火墙ip地址-->
<property name="loginoutURL" value="/***.do"/> <!--注销-->
<property name="messagesend" value="/*******.do"/><!--微信发送消息-->
</bean>
</mvc:interceptors>
<!---配置拦截器-->
<!-- 配置视图解析器。Resolves views selected for rendering by @Controllers to .jsp resources in the /WEB-INF/views directory -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!-- 前缀 -->
<property name="prefix" value="/" />
<!-- 后缀 -->
<property name="suffix" value=".jsp" />
</bean>
<!-- Spring分段文件上传所必须的 ,用于检查请求中是否包含multipart
see: http://www.html.org.cn/books/springReference/ch13s08.html
-->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
</bean>
<!--定义异常处理页面-->
<bean id="exceptionResolver" class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<property name="exceptionMappings">
<props>
<prop key="org.springframework.web.HttpSessionRequiredException">Error</prop>
<prop key="java.io.IOException">Error</prop>
<prop key="java.lang.Exception">Error</prop>
<prop key="java.sql.SQLException">Error</prop>
</props>
</property>
</bean>
<import resource="classpath:applicationContext.xml"/>
</beans>