target
掌握拦截器的定义和配置
掌握拦截器的执行流程
掌握使用拦截器实现用户登录
了解过滤器的使用场景及过滤器的实现
理解过滤器、拦截器的执行顺序
了解过滤器、拦截器的区别
在开发一个网站时可能有这样的需求:某些页面只希望几个特定的用户浏览。对于这样的访问权限控制,应该如何实现呢?拦截器就可以实现上述需求。拦截器是Spring MVC框架中重要的组成部分。
Spring MVC 的拦截器(Interceptor)与 Java Servlet 的过滤器(Filter)类似,它主要用于拦截用户的请求并做相应的处理,通常应用在权限验证、记录请求信息的日志、判断用户是否登录等功能上。
1. 拦截器的定义
在 Spring MVC 框架中定义一个拦截器需要对拦截器进行定义和配置,通过实现 HandlerInterceptor 接口
可以定义一个拦截器。
package com.lee.springmvc.interceptor;
public class TestInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
System.out.println("preHandle方法在控制器的处理请求方法调用之前执行");
return false;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
System.out.println("postHandle方法在控制器的处理请求方法调用之后,解析视图之前执行");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception {
System.out.println("afterCompletion方法在控制器的处理请求方法执行完成后执行,即视图渲染结束之后执行");
}
}
在上述拦截器的定义中实现了 HandlerInterceptor 接口,并实现了接口中的 3 个方法。有关这 3 个方法的描述如下。
-
preHandle 方法:该方法在控制器的处理请求方法前执行,其返回值表示是否中断后续操作,返回 true 表示继续向下执行,返回 false 表示中断后续操作。
-
postHandle 方法:该方法在控制器的处理请求方法调用之后、解析视图之前执行,可以通过此方法对请求域中的模型和视图做进一步的修改。
-
afterCompletion 方法:该方法在控制器的处理请求方法执行完成后执行,即视图渲染结束后执行,可以通过此方法实现一些资源清理、记录日志信息等工作。
2. 拦截器的配置
让自定义的拦截器生效需要在 Spring MVC 的配置文件中进行配置,配置示例代码如下:
在上述示例代码中, 元素用于配置一组拦截器,其子元素
定义的是全局拦截器,即拦截所有的请求。
元素中定义的是指定路径的拦截器,其子元素
用于配置拦截器作用的路径,该路径在其属性 path 中定义。
如上述示例代码中,path 的属性值/**
表示拦截所有路径,/gotoTest
表示拦截所有以/gotoTest
结尾的路径。如果在请求路径中包含不需要拦截的内容,可以通过 子元素进行配置。
需要注意的是, 元素的子元素必须按照
、
、
的顺序配置。
3. 执行流程
3.1 单个拦截器的执行流程
单个拦截器的执行流程如下:

在配置文件中如果只定义了一个拦截器,程序将首先执行拦截器类中的 preHandle
方法,如果该方法返回 true,程序将继续执行控制器中处理请求的方法,否则中断执行。如果 preHandle 方法返回 true,并且控制器中处理请求的方法执行后、返回视图前将执行 postHandle 方法,返回视图后才执行 afterCompletion 方法。
下面通过一个应用 springMVC-11 演示拦截器的执行流程,具体步骤如下:
① 将 springmvc相关jar复制到 lib下
② 创建 web.xml
在 WEB-INF 目录下创建 web.xml 文件,该文件中的配置信息如下:
SpringMVC-11index.htmlindex.jspspringDispatcherServletorg.springframework.web.servlet.DispatcherServletcontextConfigLocationclasspath:springmvc.xml1springDispatcherServlet/
③ 创建控制器类
在 src 目录下创建一个名为 controller 的包,并在该包中创建控制器类 Interceptor Controller,代码如下:
package com.lee.springmvc.controller;
@Controller
public class InterceptorController {
@RequestMapping("/gotoTest")
public String gotoTest() {
System.out.println("正在测试拦截器,执行控制器的处理请求方法中");
return "test";
}
}
④ 创建拦截器类
package com.lee.springmvc.interceptor;
public class TestInterceptor implements HandlerInterceptor {
@Override
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response, Object handler, Exception ex)
throws Exception {
System.out.println("afterCompletion方法在控制器的处理请求方法执行完成后执行,即视图渲染结束之后执行");
}
@Override
public void postHandle(HttpServletRequest request,
HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
System.out.println("postHandle方法在控制器的处理请求方法调用之后,解析视图之前执行");
}
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
System.out.println("preHandle方法在控制器的处理请求方法调用之前执行");
return true;
}
}
⑤ 创建配置文件 springmvc.xml
⑥ 创建视图 JSP文件
在 WEB-INF/views 目录下新建 test.jsp
Insert title here
视图
部署项目,访问:http://localhost:8080/SpringMVC-11/gotoTest,控制台将打印:

3.2 多个拦截器的执行流程
执行流程如下:

在 Web 应用中通常需要有多个拦截器同时工作,这时它们的 preHandle 方法将按照配置文件中拦截器的配置顺序执行,而它们的 postHandle 方法和 afterCompletion 方法则按照配置顺序的反序执行。
① 创建多个拦截器
创建两个拦截器:
Interceptor1.java:
package com.lee.springmvc.interceptor;
public class Interceptor1 implements HandlerInterceptor {
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
System.out.println("Interceptor1 preHandle 方法执行");
/** 返回true表示继续向下执行,返回false表示中断后续的操作 */
return true;
}
@Override
public void postHandle(HttpServletRequest request,
HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
System.out.println("Interceptor1 postHandle 方法执行");
}
@Override
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response, Object handler, Exception ex)
throws Exception {
System.out.println("Interceptor1 afterCompletion 方法执行");
}
}
Interceptor2.java:
package com.lee.springmvc.interceptor;
public class Interceptor2 implements HandlerInterceptor {
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
System.out.println("Interceptor2 preHandle 方法执行");
/** 返回true表示继续向下执行,返回false表示中断后续的操作 */
return true;
}
@Override
public void postHandle(HttpServletRequest request,
HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
System.out.println("Interceptor2 postHandle 方法执行");
}
@Override
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response, Object handler, Exception ex)
throws Exception {
System.out.println("Interceptor2 afterCompletion 方法执行");
}
}
② 配置拦截器
部署项目,访问:http://localhost:8080/SpringMVC-11/gotoTest,控制台打印:

思考:
访问:http://localhost:8080/SpringMVC-11/gotoTest2,控制台会打印什么?
4. 用户登录权限验证案例
要求如下:只有成功登录的用户才能访问系统的主页面 main.jsp,如果没有成功登录而直接访问主页面,则拦截器将请求拦截,并转发到登录页面 login.jsp。当成功登录的用户在系统主页面中单击“退出”链接时回到登录页面。
创建动态Web项目:SpringMVC-login
① 导jar包
commons-logging-1.2.jar
jackson-annotations-2.10.1.jar
jackson-core-2.10.1.jar
jackson-databind-2.10.1.jar
spring-aop-4.3.9.RELEASE.jar
spring-beans-4.3.9.RELEASE.jar
spring-context-4.3.9.RELEASE.jar
spring-core-4.3.9.RELEASE.jar
spring-expression-4.3.9.RELEASE.jar
spring-web-4.3.9.RELEASE.jar
spring-webmvc-4.3.9.RELEASE.jar
② 创建web.xml
SpringMVC-loginindex.htmindex.jspspringDispatcherServletorg.springframework.web.servlet.DispatcherServletcontextConfigLocationclasspath:springmvc.xml1springDispatcherServlet/
③ 创建 pojo类
package com.lee.springmvc.bean;
public class User {
private String uname;
private String upwd;
// getter、setter略
}
④ 创建控制器类
package com.lee.springmvc.controller;
@Controller
public class UserController {
/**
* 登录页面初始化
*/
@RequestMapping("/toLogin")
public String initLogin() {
return "login";
}
/**
* 处理登录功能
*/
@RequestMapping("/login")
public String login(User user, Model model, HttpSession session) {
System.out.println(user.getUname());
if ("zhangsan".equals(user.getUname()) && "123456".equals(user.getUpwd())) {
// 登录成功,将用户信息保存到session对象中
session.setAttribute("user", user);
// 重定向到主页面的跳转方法
return "redirect:main";
}
model.addAttribute("msg", "用户名或密码错误,请重新登录! ");
return "login";
}
/**
* 跳转到主页面
*/
@RequestMapping("/main")
public String toMain() {
return "main";
}
/**
* 退出登录
*/
@RequestMapping("/logout")
public String logout(HttpSession session) {
// 清除 session
session.invalidate();
return "login";
}
}
⑤ 创建拦截器类
package com.lee.springmvc.interceptor;
public class LoginInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
// 获取请求的URL
String url = request.getRequestURI();
// login.jsp或登录请求放行,不拦截
if (url.indexOf("/toLogin") >= 0 || url.indexOf("/login") >= 0) {
return true;
}
// 获取 session
HttpSession session = request.getSession();
Object user = session.getAttribute("user");
if (user != null) {
return true;
}
// 没有登录且不是登录页面,转发到登录页面,并给出提示错误信息
request.setAttribute("msg", "还没登录,请先登录!");
request.getRequestDispatcher("/WEB-INF/views/login.jsp").forward(request, response);
return false;
}
@Override
public void afterCompletion(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, Exception arg3)
throws Exception {
// TODO Auto-generated method stub
}
@Override
public void postHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, ModelAndView arg3)
throws Exception {
// TODO Auto-generated method stub
}
}
⑥ 配置拦截器
在 src 下新建springmvc.xml,并配置拦截器:
⑦ 创建 jsp页面
在 WEB-INF/views 目录下创建 login.jsp 和 main.jsp。
mian.jsp:
welcome
当前用户:${user.uname }
退出
login.jsp :
login
${msg }
用户名:
密码:
部署应用,访问:http://localhost:8080/SpringMVC-login/main,如图:

当用户没有登录而直接访问系统主页面时请求将被登录拦截器拦截,返回到登录页面,并提示信息。如果用户在用户名框中输入“zhangsan”,在密码框中输入“123456”,单击“登录”按钮后浏览器的显示结果如图 :

5. 过滤器
过滤器实际上就是对web资源进行拦截,做一些处理后再交给下一个过滤器或servlet处理
通常都是用来拦截request进行处理的,也可以对返回的response进行拦截处理。
5.1 应用场景
-
自动登录
-
统一设置编码格式
-
访问权限控制
-
敏感字符过滤等
5.2 创建过滤器
在 web.xml中用 filter标签配置:
myFiltercom.lee.filter.MyFiltermyFilter/*
MyFilter.java
通过实现接口OncePerRequestFilter来创建一个过滤器:
public class MyFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
System.out.println("请求前:" + filterChain.getClass());
filterChain.doFilter(request, response);
System.out.println("响应时:" + filterChain.getClass());
}
}
filterChain.doFilter(request, response):表示放行请求。
filterChain.doFilter(request, response)之前代码:表示放行请求前执行的一些操作。
filterChain.doFilter(request, response)之后代码:表示响应时执行的操作。
5.3 登录案例
public class MyFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
HttpSession session = httpServletRequest.getSession();
if (session.getAttribute("login") == null) { //没有登陆
httpServletResponse.sendRedirect("/login");
} else {//已登录
System.out.println("ip:" + httpServletRequest.getRemoteHost() + ",url:" + httpServletRequest.getRequestURL());
// 将请求放行
filterChain.doFilter(servletRequest, servletResponse);
}
}
}
5.4 urlPatterns
配置要拦截的资源
-
以指定资源匹配。例如
"/index.jsp"
-
以目录匹配。例如
"/servlet/*"
-
以后缀名匹配,例如
"*.jsp"
-
通配符,拦截所有web资源。
"/*"
6. 过滤器、拦截器执行顺序
当客户端的一个请求发送到服务端时,会被过滤器和拦截器所拦截。他们过滤拦截的顺序是: