
SpringMVC的拦截器(Interceptor)也是AOP思想的一种实现方式。它与Servlet的过滤器(Filter)功能类似,主要用于拦截用户的请求并做相应的处理,通常应用在权限验证、记录请求信息的日志、判断用户是否登录等功能上。
拦截器和过滤器的区别
- 拦截器是SpringMVC组件,而过滤器是Servlet组件。
- 拦截器不依赖Web容器,过滤器依赖Web容器。
- 拦截器只能对控制器请求起作用,而过滤器则可以对所有的请求起作用。
- 拦截器可以直接获取IOC容器中的对象,而过滤器就不太方便获取。
接下来我们使用SpringMVC拦截器,首先使用maven创建SprinMVC的web项目
- package com.example.controller;
-
- import org.springframework.stereotype.Controller;
- import org.springframework.ui.Model;
- import org.springframework.web.bind.annotation.CrossOrigin;
- import org.springframework.web.bind.annotation.RequestMapping;
- import org.springframework.web.bind.annotation.ResponseBody;
-
- @Controller
- public class MyController1 {
- @RequestMapping ("/m1")
- public String m1(){
- System.out.println("控制器方法");
- return "result";
- }
- }
创建拦截器类,该类实现HandlerInterceptor接口,需要重写三个方法:
- preHandle:请求到达Controller前执行的方法,返回值为true通过拦截器,返回值为false被拦截器拦截。
- postHandle:跳转到JSP前执行的方法
- afterCompletion:跳转到JSP后执行的方法
- package com.example.interceptor;
-
- import org.springframework.web.servlet.HandlerInterceptor;
- import org.springframework.web.servlet.ModelAndView;
-
- import javax.servlet.http.HttpServletRequest;
- import javax.servlet.http.HttpServletResponse;
- import java.util.Scanner;
-
- public class MyInterceptor implements HandlerInterceptor {
- // 请求到达Controller前执行
- @Override
- public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
- System.out.print("请求到达Controller前\t");
- // 如果return false则无法到达Controller
- // 控制台输入决定是否进入Controller
- System.out.print("控制台输入决定是否进入Controller: ");
- Scanner scanner = new Scanner(System.in);
- boolean flag;
- flag = scanner.nextBoolean();
- return flag;
- }
-
- // 跳转JSP前执行,此时可以向Request域添加数据
- @Override
- public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
- System.out.print("跳转JSP前\t");
- /*System.out.print("控制台输入决定是否添加数据: ");
- Scanner scanner = new Scanner(System.in);
- boolean flag;
- flag = scanner.nextBoolean();
- if(flag)*/
- request.setAttribute("name","HQX");
- }
-
- // 跳转JSP后执行,此时已经不能向Request域添加数据
- @Override
- public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
- System.out.println("跳转到JSP后");
- request.setAttribute("age",10);
- }
- }
OK,首先我们这里到达控制器前和是否进入控制器还有是否跳转JSP,跳转到JSP后都有对应的提示。
result.jsp
- <%@ page contentType="text/html;charset=UTF-8" language="java" %>
- <html>
- <head>
- <title>结果title>
- head>
- <body>
- <h3>name:${requestScope.name}h3>
- <h3>age:${requestScope.age}h3>
- body>
- html>
这里把我们控制台输入的name响应到前端页面,但是age注定是没有属性的,因为跳转到JSP后才添加注定是没有意义的。
接下来我们需要在SpringMVC核心配置文件中配置拦截器
- <mvc:interceptors>
- <mvc:interceptor>
-
- <mvc:mapping path="/**"/>
-
- <bean class="com.itbaizhan.interceptor.MyInterceptor"/>
- mvc:interceptor>
- mvc:interceptors>
OK,第一次输入true后后面的提示信息也是可以出来的。已经成功拦截了

全局拦截器可以拦截所有控制器处理的URL,作用等于/**,配置方式如下:
<mvc:interceptors> <bean class="com.itbaizhan.interceptor.MyInterceptor"> bean> mvc:interceptors>
如果一个URL能够被多个拦截器所拦截,全局拦截器最先执行,其他拦截器根据配置文件中配置的从上到下执行,但是我实操下来发现并不是这样。接下来我来验证一下我的想法,再配置一个拦截器2:
- package com.example.interceptor;
-
- import org.springframework.web.servlet.HandlerInterceptor;
- import org.springframework.web.servlet.ModelAndView;
-
- import javax.servlet.http.HttpServletRequest;
- import javax.servlet.http.HttpServletResponse;
- import java.util.Scanner;
-
- public class MyInterceptor2 implements HandlerInterceptor {
- @Override
- public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
- System.out.print("拦截器2:请求到达Controller前\t");
- // 如果return false则无法到达Controller
- // 控制台输入决定是否进入Controller
- System.out.print("控制台输入决定是否进入Controller: ");
- Scanner scanner = new Scanner(System.in);
- boolean flag;
- flag = scanner.nextBoolean();
- return flag;
- }
-
- // 跳转JSP前执行,此时可以向Request域添加数据
- @Override
- public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
- System.out.print("拦截器2:跳转JSP前\t");
- /*System.out.print("控制台输入决定是否添加数据: ");
- Scanner scanner = new Scanner(System.in);
- boolean flag;
- flag = scanner.nextBoolean();
- if(flag)*/
- request.setAttribute("age","10");
- }
-
- // 跳转JSP后执行,此时已经不能向Request域添加数据
- @Override
- public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
- System.out.println("拦截器2:跳转到JSP后");
- request.setAttribute("age",10);
- }
- }
这里再配置一个拦截器,为了更能体现拦截器的拦截顺序。
-
- <mvc:interceptors>
-
- <mvc:interceptor>
-
- <mvc:mapping path="/m1"/>
-
- <bean class="com.example.interceptor.MyInterceptor"/>
- mvc:interceptor>
-
- <mvc:interceptor>
-
- <mvc:mapping path="/m1"/>
-
- <bean class="com.example.interceptor.MyInterceptor2"/>
- mvc:interceptor>
-
- <bean class="com.example.interceptor.GlobalInterceptor"/>
- mvc:interceptors>
我们这里测试的拦截器1,2拦截路径都是/m1,我们把全局拦截器放在最后看一下执行顺序是如何的,如果按照上面的说法的话,则应该先提示全局拦截器,再拦截器1,拦截器2的提示信息。接下来我们来看一下实际结果吧。

我们可以看到当访问/m1的时候,首先进入控制器前出现的顺序是拦截器1,然后拦截器2,最后是全局拦截器,然后跳转JSP前的顺序才是全局拦截器、拦截器2,拦截器1,跳转JSP后的也是如此。
接下来我们编写一个拦截器案例,需求如下:
在系统中,我们需要将所有响应中的一些敏感词替换为 *** ,此时可以使用拦截器达到要求:
- @RequestMapping("/m2")
- public String m2(Model model){
- model.addAttribute("name","大笨蛋");
- return "result";
- }
- package com.example.interceptor;
-
- import org.springframework.web.servlet.HandlerInterceptor;
- import org.springframework.web.servlet.ModelAndView;
-
- import javax.servlet.http.HttpServletRequest;
- import javax.servlet.http.HttpServletResponse;
- import java.util.Map;
- import java.util.Set;
-
- public class SensitiveWordInterceptor implements HandlerInterceptor {
- @Override
- public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
- return true;
- }
-
- @Override
- public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
- // 敏感词列表
- String[] sensitiveWords = {"坏人","暴力","笨蛋"};
- // model中所有数据
-
- if(modelAndView!=null) {
- Map
model = modelAndView.getModel(); - Set
> entries = model.entrySet(); -
- // 遍历model
- for (Map.Entry
entry : entries) { - String key = entry.getKey();
- String value = entry.getValue().toString();
- // 将model值和敏感词列表遍历比对
- for (String sensitiveWord : sensitiveWords) {
- // 如果model包含敏感词,则替换
- if (value.contains(sensitiveWord)) {
- String newStr = value.replaceAll(sensitiveWord, "***");
- model.put(key, newStr);
- }
- }
- }
- }
- }
-
- @Override
- public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
- }
- }
-
- <mvc:interceptor>
- <mvc:mapping path="/**"/>
- <bean class="com.example.interceptor.SensitiveWordInterceptor"/>
- mvc:interceptor>

OK,我们可以发现笨蛋确实是被换成了***。
同源策略是浏览器的一个安全功能。同源,指的是两个URL的协议,域名,端口相同。浏览器出于安全方面的考虑,不同源的客户端脚本在没有明确授权的情况下,不能读写对方资源。
哪些不受同源策略限制:
- 页面中的 跳转、表单提交不会受到同源策略限制的。
- 静态资源引入也不会受到同源策略限制。如嵌入到页面中的