八、SpringMVC 拦截器
8.1 什么是拦截器
在系统中,经常需要在处理用户请求之前和之后执行一些行为,例如检测用户的权限,或者将请求的信息记录到日志中,即平时所说的“权限检测”及“日志记录”。当然不仅仅这些,所以需要一种机制,拦截用户的请求,在请求的前后添加处理逻辑。
Spring MVC 提供了 Interceptor 拦截器机制,用于请求的预处理和后处理。
在开发一个网站时可能有这样的需求:某些页面只希望几个特定的用户浏览。对于这样的访问权限控制,应该如何实现呢?拦截器就可以实现上述需求。在 Struts2 框架中,拦截器是其重要的组成部分,Spring MVC 框架也提供了拦截器功能。
Spring MVC 的拦截器(Interceptor)与 Java Servlet 的过滤器(Filter)类似,它主要用于拦截用户的请求并做相应的处理,通常应用在权限验证、记录请求信息的日志、判断用户是否登录等功能上。
将拦截器按一定的顺序联结成一条链,这条链称为拦截器链(Interceptor Chain)。在访问被拦截的方法或字段时,拦截器链中的拦截器就会按其之前定义的顺序被调用。拦截器也是AOP思想的具体实现。
拦截器和过滤器的区别:
组件 | 过滤器Filter | 拦截器 Interceptor |
---|
来源 | 来源于Servlet,重量级的组件 | 来源于SpringMVC框架,只能用在这个框架中 | 拦截范围 | 所有的资源:Servlet,JSP,HTML | 只能拦截处理器 |
8.2 maven依赖
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>4.3.18.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>4.3.18.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.18.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
<version>4.3.18.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>4.3.18.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>4.3.18.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>4.3.18.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>4.3.18.RELEASE</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<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>
8.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>
<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>
<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">
<context:component-scan base-package="com.newcapec"/>
<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>
8.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";
}
}
8.5 拦截器定义
实现HandlerInterceptor接口,并且实现其中三个抽象方法。
public class Demo1Interceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("Demo1Interceptor拦截器中的preHandle方法执行了...");
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("Demo1Interceptor拦截器中的postHandle方法执行了...");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("Demo1Interceptor拦截器中的afterCompletion方法执行了...");
}
}
方法解析:
1、preHandle方法
在请求进入Controller方法之前调用此方法,起到拦截的作用;
如果preHandle方法返回值为true,表示放行(允许进入Controller方法);
如果preHandle方法返回值为false,表示拦截(不允许进入Controller方法);
2、postHandle方法
请求进入Controller方法之后,但未结束之前,调用此方法;
可在返回的模型数据进行处理加工,比如加入公用信息以便页面显示;
3、afterCompletion方法
请求离开Controller方法之后,调用此方法;
可获取异常信息,记录日志,资源清理等;
执行流程解析:
后端控制器方法执行之前,先执行拦截器preHandle(),后端控制器方法执行结束之后,执行的是拦截器的postHandle(),当前端控制器解析控制层返回的ModelandView并交给视图解析器渲染解析完成后,才会执行拦截器的afterCompletion()方法。
8.6 拦截器配置
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<bean class="com.newcapec.interceptor.Demo1Interceptor"/>
</mvc:interceptor>
</mvc:interceptors>
/* 与 /** 的区别:
请求路径 | 解析 |
---|
/find | 配置/*或/**可进入 | /demo/find | 配置/**可进入 |
8.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.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.8.1 正常运行流程
每个拦截器的preHandler方法都返回true。
执行结果:
Demo1Interceptor拦截器中的preHandle方法执行了...
第二个拦截器中的preHandle方法执行了...
DemoController的find方法...
第二个拦截器中的postHandle方法执行了...
Demo1Interceptor拦截器中的postHandle方法执行了...
第二个拦截器中的afterCompletion方法执行了...
Demo1Interceptor拦截器中的afterCompletion方法执行了...
8.8.2 中断流程
Demo1Interceptor的preHandler方法返回true,Demo2Interceptor返回false。
执行结果:
Demo1Interceptor拦截器中的preHandle方法执行了...
第二个拦截器中的preHandle方法执行了...
Demo1Interceptor拦截器中的afterCompletion方法执行了...
8.8.3 中断流程
每个拦截器的preHandler方法都返回false。
执行结果:
Demo1Interceptor拦截器中的preHandle方法执行了...
8.8.4 总结
- preHandle方法按拦截器定义顺序调用,postHandler方法按拦截器定义逆序调用,afterCompletion方法按拦截器定义逆序调用。
- postHandler方法在拦截器链内所有拦截器返true时调用(处理器方法执行了,才执行)。
- afterCompletion方法只要preHandle方法执行过,并且返回true时就会调用。
8.9 拦截器应用
用户身份认证拦截器的实现。
8.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.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";
}
}
8.9.2 拦截器
public class LoginInterceptor implements HandlerInterceptor {
@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 {
}
}
8.9.3 配置
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<mvc:exclude-mapping path="/auth/login"/>
<bean class="com.newcapec.interceptor.LoginInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
8.9.4 登录页面
login.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 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>
vc:interceptor> </mvc:interceptors>
### 8.9.4 登录页面
**login.jsp:**
```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 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>
|