在上一篇文章中,介绍了 Interceptor 和 Filter 的区别和执行顺序。我们可以知道 Filter 依赖于 Servlet,它主要是针对 URL 地址做一个编码的事情、过滤掉没用的参数、简单的安全校验(比如登录不登录之类),而 Interceptor 依赖于 SpringMVC 框架,它是在 service 或者一个方法调用前,调用一个方法,或者在方法调用后,调用一个方法。下面来具体介绍 Interceptor 的用法和使用场景。
(1)、实现HandlerInterceptor接口。
(2)、继承实现了HandlerInterceptor接口的类。比如Spring已经提供的实现了HandlerInterceptor接口的
抽象类HandlerInterceptorAdapter ;
该接口中定义了三个方法,我们就是通过这三个方法来对用户的请求进行拦截处理的。
(1)、preHandle (HttpServletRequest request, HttpServletResponse response, Object handle) 方法。顾名思义,该方法将在请求处理之前进行调用。SpringMVC中的Interceptor是链式的调用的,在一个应用中或者是在一个请求中可以同时存在多个Interceptor。每个Interceptor的调用会依据它的声明顺序依次执行,而且最先执行的都是Interceptor中的preHandle 方法,所以可以在这个方法中进行一些前置初始化操作或者是对当前请求的一个预处理,也可以在这个方法中进行一些判断来决定请求是否要继续进行下去。该方法的返回值是Boolean类型的,当它返回为false时,表示请求结束,后续的Interceptor和Controller都不会再执行;当返回值为true 时就会继续调用下一个Interceptor的preHandle方法,如果已经是最后一个Interceptor的时候就会是调用当前请求的Controller 方法。
(2)、postHandle (HttpServletRequest request, HttpServletResponse response, Object handle, ModelAndView modelAndView) 方法。由preHandle方法的解释我们知道这个方法包括后面要说到的afterCompletion方法都只能是在当前所属的Interceptor的preHandle方法的返回值为true时才能被调用。**postHandle方法,顾名思义就是在当前请求进行处理之后,也就是Controller方法调用之后执行,但是它会在DispatcherServlet进行视图返回渲染之前被调用,所以我们可以在这个方法中对Controller处理之后的ModelAndView对象进行操作。**postHandle方法被调用的方向跟preHandle是相反的,也就是说先声明的Interceptor的postHandle方法反而会后执行。
(3)、afterCompletion (HttpServletRequest request, HttpServletResponse response, Object handle, Exception ex) 方法,该方法也是需要当前对应的Interceptor的preHandle方法的返回值为true时才会执行。顾名思义,该方法将在整个请求结束之后,也就是在DispatcherServlet 渲染了对应的视图之后执行。这个方法的主要作用是用于进行资源清理工作的。
上面对Interceptor的三个方法进行了详细的介绍,下面我们就来看看Interceptor的使用场景。
//登陆拦截器
public class LoginInterceptor implements HandlerInterceptor {
/**
* 在控制器执行之前完成业务逻辑操作
* 方法的返回值决定逻辑是否继续执行, true,表示继续执行, false, 表示不再继续执行。
*/
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
// 判断当前用户是否已经登陆
HttpSession session = request.getSession();
User loginUser = (User)session.getAttribute("loginUser");
if ( loginUser == null ) {
String path = session.getServletContext().getContextPath();
response.sendRedirect(path + "/login");
return false;
} else {
return true;
}
}
/**
* 在Controller方法调用之后执行,但是它会在DispatcherServlet进行视图返回渲染之前被调用
*/
public void postHandle(HttpServletRequest request,
HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
// TODO Auto-generated method stub
}
/**
* 在完成视图渲染之后,执行此方法。
*/
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response, Object handler, Exception ex)
throws Exception {
}
}
登陆拦截器在访问每个URL的时候都会进行是否登陆判断,如果没登陆的话,就直接跳转到登陆页,并不会跳转到要访问的路径(数据可能获取不到)。在登陆的控制器中,需要把用户信息放在session中,然后在登陆拦截器中,采用如下方式获取HttpSession session = request.getSession();
//登陆控制器,往session中存用户数据代码
User dbUser = userService.queryForLogin(user);
if ( dbUser != null ) {
session.setAttribute("loginUser", dbUser);
}
/**
* 用于记录日志,在每次请求之前,打印请求的地址和参数,方便调试
*/
public class LogInterceptor implements HandlerInterceptor {
/** 记录日志 */
private static Logger logger = LogManager.getLogger(LogManager.ROOT_LOGGER_NAME);
@Override
public boolean preHandle(HttpServletRequest request,HttpServletResponse response,
Object arg2) throws Exception {
StringBuilder sb = new StringBuilder();
String uri = request.getRequestURI();
sb.append("---------------> demo uri:").append(uri).append(" - ");
Enumeration<String> enums2 = request.getParameterNames();
while (enums2.hasMoreElements()) {
String key = enums2.nextElement();
sb.append("\"").append(key).append("\":").append(
request.getParameter(key)).append(", ");
}
logger.info(sb.toString());
return true;
}
@Override
public void postHandle(HttpServletRequest request,HttpServletResponse response,
Object arg2, ModelAndView arg3)throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest request,HttpServletResponse response,
Object arg2, Exception arg3) throws Exception {
}
}
此处需要说明一下,无论在门户网站还是管理平台,如果登陆系统后,你知道一些存在的url,即使你没有访问权限,但你在浏览器中直接输入正确的url,你依然可以访问页面,此拦截器的目的就是解决这个问题。
//此处采用继承抽象类的方式,不是实现Interceptor方式
public class AuthInterceptor extends HandlerInterceptorAdapter {
@Autowired
private PermissionService permissionService;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
Object handler) throws Exception {
// 获取用户的请求地址
String uri = request.getRequestURI();
String path = request.getSession().getServletContext().getContextPath();
// 判断当前路径是否需要进行权限验证。
// 查询所有需要验证的路径集合
List<Permission> permissions = permissionService.queryAll();
Set<String> uriSet = new HashSet<String>();
for ( Permission permission : permissions ) {
if ( permission.getUrl() != null && !"".equals(permission.getUrl()) ) {
uriSet.add(path + permission.getUrl());
}
}
if ( uriSet.contains(uri) ) {
// 权限验证
// 判断当前用户是否拥有对应的权限
Set<String> authUriSet = (Set<String>)request.getSession()
.getAttribute("authUriSet");
if ( authUriSet.contains(uri) ) {
return true;
} else {
response.sendRedirect(path + "/error");
return false;
}
} else {
return true;
}
}
}
<mvc:interceptors>
//对所有请求进行拦截,包括静态资源
<mvc:interceptor>
<mvc:mapping path="/**"/>
<bean class="com.scorpios.atcrowdfunding.web.LogInterceptor">bean>
mvc:interceptor>
<mvc:interceptor>
<mvc:mapping path="/**" />
<mvc:exclude-mapping path="/login" />
<mvc:exclude-mapping path="/doAJAXLogin" />
<mvc:exclude-mapping path="/bootstrap/**" />
<mvc:exclude-mapping path="/css/**" />
<mvc:exclude-mapping path="/fonts/**" />
<mvc:exclude-mapping path="/img/**" />
<mvc:exclude-mapping path="/jquery/**" />
<mvc:exclude-mapping path="/layer/**" />
<mvc:exclude-mapping path="/script/**" />
<mvc:exclude-mapping path="/ztree/**" />
//该拦截器不对静态资源进行拦截
<bean class="com.scorpios.atcrowdfunding.web.LoginInterceptor">bean>
mvc:interceptor>
<mvc:interceptor>
<mvc:mapping path="/**" />
<mvc:exclude-mapping path="/login" />
<mvc:exclude-mapping path="/doAJAXLogin" />
<mvc:exclude-mapping path="/bootstrap/**" />
<mvc:exclude-mapping path="/css/**" />
<mvc:exclude-mapping path="/fonts/**" />
<mvc:exclude-mapping path="/img/**" />
<mvc:exclude-mapping path="/jquery/**" />
<mvc:exclude-mapping path="/layer/**" />
<mvc:exclude-mapping path="/script/**" />
<mvc:exclude-mapping path="/ztree/**" />
//该拦截器不对静态资源进行拦截
<bean class="com.scorpios.atcrowdfunding.web.AuthInterceptor">bean>
mvc:interceptor>
mvc:interceptors>
以上就是拦截器Interceptor的使用和使用场景。
(1)、在SpringMVC中利用mvc:interceptors标签,来声明一系列的拦截器,然后它们就可以形成一个拦截器链,拦截器的执行顺序是按声明的先后顺序执行的,先声明的拦截器中的preHandle方法会先执行,然而它的postHandle方法和afterCompletion方法却会后执行。
(2)、在mvc:interceptors标签下声明interceptor主要有两种方式:
a、直接定义一个Interceptor实现类的bean对象。使用这种方式声明的Interceptor拦截器将会对所有的请求进行拦
截。
b、使用mvc:interceptor标签进行声明。使用这种方式进行声明的Interceptor可以通过mvc:mapping子标签来定义
需要进行拦截的请求路径。
经过上述两步之后,定义的拦截器就会发生作用对特定的请求进行拦截了。