拦截器
1. 什么是拦截器
SpringMVC的处理器拦截器类似于Servlet开发中的过滤器Filter,用于对处理器进行预处理和后处理
2. maven依赖
<dependencies>
<!-- spring核心包-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>4.3.18.RELEASE</version>
</dependency>
<!-- springbean包 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>4.3.18.RELEASE</version>
</dependency>
<!-- springcontext包 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.18.RELEASE</version>
</dependency>
<!-- spring表达式包 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
<version>4.3.18.RELEASE</version>
</dependency>
<!-- springAOP包 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>4.3.18.RELEASE</version>
</dependency>
<!-- springAspects包 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>4.3.18.RELEASE</version>
</dependency>
<!-- spring对web的支持 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>4.3.18.RELEASE</version>
</dependency>
<!-- springwebMVC -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>4.3.18.RELEASE</version>
</dependency>
<!-- 配置javaweb环境 -->
<!-- servlet-api -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<!-- jsp-api -->
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.1</version>
<scope>provided</scope>
</dependency>
<!-- jstl -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<!-- jackson -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.9.5</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.5</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.9.5</version>
</dependency>
</dependencies>
3. SpringMVC配置
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<!--字符编码过滤器-->
<filter>
<filter-name>CharacterEncodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CharacterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- 通过隐藏域传参解决form表单的PUT与DELETE方式的请求 -->
<filter>
<filter-name>HiddenHttpMethodFilter</filter-name>
<filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>HiddenHttpMethodFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- 解决put请求传递参数的问题-->
<filter>
<filter-name>HttpPutFormContentFilter</filter-name>
<filter-class>org.springframework.web.filter.HttpPutFormContentFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>HttpPutFormContentFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- 配置前端控制器 -->
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>/views/index.jsp</welcome-file>
</welcome-file-list>
</web-app>
springmvc.xml
<?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:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="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.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!-- springmvc的注解式开发 -->
<!-- 开启组件扫描 -->
<context:component-scan base-package="com.newcapec"/>
<!-- MVC注解驱动 -->
<mvc:annotation-driven/>
<!-- 配置视图解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!-- 配置视图地址的前缀和后缀:简化视图地址 -->
<property name="prefix" value="/views/"/>
<property name="suffix" value=".jsp"/>
</bean>
<!--不拦截静态资源-->
<mvc:default-servlet-handler/>
</beans>
4. Controller
DemoController.java
@Controller
@RequestMapping("/demo")
public class DemoController {
@RequestMapping("/find")
public String find(){
System.out.println("DemoController的find方法...");
return "success";
}
@RequestMapping("/add")
public String add(){
System.out.println("DemoController的add方法...");
return "success";
}
@RequestMapping("/edit")
public String edit(){
System.out.println("DemoController的edit方法...");
return "success";
}
@RequestMapping("/remove")
public String remove(){
System.out.println("DemoController的remove方法...");
return "success";
}
}
EmpController.java
@Controller
@RequestMapping("/emp")
public class EmpController {
@RequestMapping("/find")
public String find(){
System.out.println("EmpController的find方法...");
return "success";
}
@RequestMapping("/add")
public String add(){
System.out.println("EmpController的add方法...");
return "success";
}
@RequestMapping("/edit")
public String edit(){
System.out.println("EmpController的edit方法...");
return "success";
}
@RequestMapping("/remove")
public String remove(){
System.out.println("EmpController的remove方法...");
return "success";
}
}
5. 拦截器定义
实现HandlerInterceptor接口,并且实现其中三个抽象方法
/**
* springmvc中的拦截器对象
* 1.编写代码:实现拦截器接口 HandlerInterceptor
* 2.配置拦截器
*/
public class Demo1Interceptor implements HandlerInterceptor {
/**
* preHandle方法,预处理
* 作用:在请求进入指定Controller方法之前,执行的方法(对请求进行验证)
* @param request 请求对象
* @param response 响应对象
* @param handler 请求将要执行的Controller对象
* @return 布尔类型:true表示请求放行,继续向后执行(有可能进入下一个拦截器,也有可能进入Controller)
* false表示拦截请求,请求不能继续向后执行
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("Demo1Interceptor拦截器中的preHandle方法执行了...");
return true;
}
/**
* postHandle 后处理
* @param request 请求对象
* @param response 响应对象
* @param handler 正在执行的Controller对象
* @param modelAndView 模型和视图数据,Controller方法执行完成之后的返回值
*
* 注意:此方法必须在请求进入Controller之后才会执行,如果没有进入Controller是不会执行的
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("Demo1Interceptor拦截器中的postHandle方法执行了...");
}
/**
* afterCompletion 请求处理完成之后
* @param request 请求对象
* @param response 响应对象
* @param handler 已经执行过的Controller对象
* @param ex Controller方法抛出的异常对象
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("Demo1Interceptor拦截器中的afterCompletion方法执行了...");
}
}
方法解析:
- preHandle方法
在请求进入Controller方法之前调用此方法,起到拦截的作用
如果preHandle方法返回值为true,表示放行(允许进入Controller方法)
如果preHandle方法返回值为false,表示拦截(不允许进入Controller方法) - postHandle方法
请求进入Controller方法之后,但未结束之前,调用此方法
可在返回的模型数据进行处理加工,比如加入公用信息以便页面显示 - afterCompletion方法
请求离开Controller方法之后,调用此方法
可获取异常信息,记录日志,资源清理等
6. 拦截器配置
<!-- 配置拦截器 -->
<mvc:interceptors>
<!-- 一个拦截器 -->
<mvc:interceptor>
<!-- 配置此拦截器所拦截的路径
拦截所有请求:/** 表示所有进入springmvc的请求
-->
<mvc:mapping path="/**"/>
<!-- 注册拦截器对象 -->
<bean class="com.newcapec.interceptor.Demo1Interceptor"/>
</mvc:interceptor>
</mvc:interceptors>
/* 与 /**的区别:
请求路径 | 解析 |
---|---|
/find | 配置/*或/**可进入 |
/demo/find | 配置/**可进入 |
7. 页面
index.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<base href="${pageContext.request.contextPath}/">
<title>Title</title>
</head>
<body>
<div style="text-align: center">
<h1>拦截器</h1>
<h2>当前登录用户:${sessionScope.loginUser}</h2>
<a href="demo/find">测试查询</a><br>
<a href="demo/add">测试新增</a><br>
<a href="demo/edit">测试编辑</a><br>
<a href="demo/remove">测试删除</a><br>
<a href="emp/find">员工查询</a><br>
<a href="emp/add">员工新增</a><br>
<a href="emp/edit">员工编辑</a><br>
<a href="emp/remove">员工删除</a><br>
<a href="auth/logout">退出系统</a><br>
</div>
</body>
</html>
success.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<base href="${pageContext.request.contextPath}/">
<title>Title</title>
</head>
<body>
<div style="text-align: center">
<h1>操作成功</h1>
</div>
</body>
</html>
8. 拦截器执行流程
定义第二个拦截器Demo2Interceptor.java
public class Demo2Interceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("第二个拦截器中的preHandle方法执行了...");
return true;
}
@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方法执行了...");
}
}
配置:
<!-- 多个拦截器的执行顺序,是按照配置的先后顺序 -->
<mvc:interceptor>
<mvc:mapping path="/**"/>
<bean class="com.newcapec.interceptor.Demo2Interceptor"/>
</mvc:interceptor>
8.1 正常运行流程
每个拦截器的preHandler方法都返回true
8.2 中断流程
Demo1Interceptor的preHandler方法返回true,Demo2Interceptor返回false
8.3 中断流程
每个拦截器的preHandler方法都返回false
8.4 总结
- preHandle方法按拦截器定义顺序调用,postHandler方法按拦截器定义逆序调用,afterCompletion方法按拦截器定义逆序调用
- postHandler方法在拦截器链内所有拦截器返true时调用
- afterCompletion方法只要preHandle方法执行过,并且返回true时就会调用
9. 拦截器应用
用户身份认证拦截器的实现
9.1 Controller
@Controller
@RequestMapping("/auth")
public class LoginController {
@RequestMapping("/login")
public String login(String username, String password, HttpSession session, Model model){
//模拟查询:根据用户名和密码查询数据
if("admin".equals(username) && "123".equals(password)) {
//比对成功,用户登录
//将用户信息放入session域对象
session.setAttribute("loginUser", username);
return "index";
}
model.addAttribute("message", "username or password is invalid");
return "login";
}
@RequestMapping("/logout")
public String logout(HttpSession session){
session.removeAttribute("loginUser");
session.invalidate();
return "login";
}
}
9.2 拦截器
public class LoginInterceptor implements HandlerInterceptor {
/**
* 身份认证:
* 过滤器:
* 1.从session域中获取用户的登录信息
* 2.判断用户信息是否为空
* 3.不为空(表示已登录)放行(doFilter方法)
* 4.为空(表示未登录)
* 5.判断当前请求是否为登录请求或无需登录可执行请求(身份认证白名单)
* 6.如果为白名单请求,放行
* 7.如果不是,拦截,跳转login,信息提示...
*
* 拦截器:
* 1.从session域中获取用户的登录信息
* 2.判断用户信息是否为空
* 3.不为空(表示已登录)放行(返回true)
* 4.为空(表示未登录),拦截,跳转login,信息提示...
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("身份认证拦截器执行.....");
HttpSession session = request.getSession();
Object userInfo = session.getAttribute("loginUser");
if(userInfo == null){
request.setAttribute("message", "you are not login.");
request.getRequestDispatcher("/views/login.jsp").forward(request, response);
return false;
}
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
}
}
9.3 配置
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<!-- 配置拦截器不拦截路径,可使用通配符 -->
<mvc:exclude-mapping path="/auth/login"/>
<!--<mvc:exclude-mapping path="/demo/**"/>-->
<bean class="com.newcapec.interceptor.LoginInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
9.4 页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<base href="${pageContext.request.contextPath}/">
<title>Title</title>
</head>
<body>
<div style="text-align: center">
<h1>用户登录</h1>
<div style="color: red;">${message}</div>
<form action="auth/login" method="post">
<p>用户名:<input type="text" name="username"></p>
<p>密 码:<input type="text" name="password"></p>
<p><button>登录</button></p>
</form>
</div>
</body>
</html>