码农知识堂 - 1000bd
  •   Python
  •   PHP
  •   JS/TS
  •   JAVA
  •   C/C++
  •   C#
  •   GO
  •   Kotlin
  •   Swift
  • Spring MVC拦截器和跨域请求


    一、拦截器简介

    SpringMVC的拦截器(Interceptor)也是AOP思想的一种实现方式。它与Servlet的过滤器(Filter)功能类似,主要用于拦截用户的请求并做相应的处理,通常应用在权限验证、记录请求信息的日志、判断用户是否登录等功能上。
    拦截器和过滤器的区别

    1. 拦截器是SpringMVC组件,而过滤器是Servlet组件。
    2. 拦截器不依赖Web容器,过滤器依赖Web容器。
    3. 拦截器只能对控制器请求起作用,而过滤器则可以对所有的请求起作用。
    4. 拦截器可以直接获取IOC容器中的对象,而过滤器就不太方便获取。 

    二、拦截器使用

    接下来我们使用SpringMVC拦截器,首先使用maven创建SprinMVC的web项目

    2.1 控制器方法

    1. package com.example.controller;
    2. import org.springframework.stereotype.Controller;
    3. import org.springframework.ui.Model;
    4. import org.springframework.web.bind.annotation.CrossOrigin;
    5. import org.springframework.web.bind.annotation.RequestMapping;
    6. import org.springframework.web.bind.annotation.ResponseBody;
    7. @Controller
    8. public class MyController1 {
    9. @RequestMapping ("/m1")
    10. public String m1(){
    11. System.out.println("控制器方法");
    12. return "result";
    13. }
    14. }

    2.2 编写拦截器类

    创建拦截器类,该类实现HandlerInterceptor接口,需要重写三个方法:

    1. preHandle:请求到达Controller前执行的方法,返回值为true通过拦截器,返回值为false被拦截器拦截。
    2. postHandle:跳转到JSP前执行的方法
    3. afterCompletion:跳转到JSP后执行的方法
    1. package com.example.interceptor;
    2. import org.springframework.web.servlet.HandlerInterceptor;
    3. import org.springframework.web.servlet.ModelAndView;
    4. import javax.servlet.http.HttpServletRequest;
    5. import javax.servlet.http.HttpServletResponse;
    6. import java.util.Scanner;
    7. public class MyInterceptor implements HandlerInterceptor {
    8. // 请求到达Controller前执行
    9. @Override
    10. public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    11. System.out.print("请求到达Controller前\t");
    12. // 如果return false则无法到达Controller
    13. // 控制台输入决定是否进入Controller
    14. System.out.print("控制台输入决定是否进入Controller: ");
    15. Scanner scanner = new Scanner(System.in);
    16. boolean flag;
    17. flag = scanner.nextBoolean();
    18. return flag;
    19. }
    20. // 跳转JSP前执行,此时可以向Request域添加数据
    21. @Override
    22. public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
    23. System.out.print("跳转JSP前\t");
    24. /*System.out.print("控制台输入决定是否添加数据: ");
    25. Scanner scanner = new Scanner(System.in);
    26. boolean flag;
    27. flag = scanner.nextBoolean();
    28. if(flag)*/
    29. request.setAttribute("name","HQX");
    30. }
    31. // 跳转JSP后执行,此时已经不能向Request域添加数据
    32. @Override
    33. public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
    34. System.out.println("跳转到JSP后");
    35. request.setAttribute("age",10);
    36. }
    37. }

     OK,首先我们这里到达控制器前和是否进入控制器还有是否跳转JSP,跳转到JSP后都有对应的提示。 

    2.3 JSP页面

    result.jsp

    1. <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    2. <html>
    3. <head>
    4. <title>结果title>
    5. head>
    6. <body>
    7. <h3>name:${requestScope.name}h3>
    8. <h3>age:${requestScope.age}h3>
    9. body>
    10. html>

    这里把我们控制台输入的name响应到前端页面,但是age注定是没有属性的,因为跳转到JSP后才添加注定是没有意义的。

    2.4 配置拦截器

    接下来我们需要在SpringMVC核心配置文件中配置拦截器

    1. <mvc:interceptors>
    2.   <mvc:interceptor>    
    3.        
    4.     <mvc:mapping path="/**"/>    
    5.        
    6.     <bean class="com.itbaizhan.interceptor.MyInterceptor"/>
    7.   mvc:interceptor>
    8. mvc:interceptors>

    2.5 测试结果

    OK,第一次输入true后后面的提示信息也是可以出来的。已经成功拦截了

    2.6 全局拦截器

    全局拦截器可以拦截所有控制器处理的URL,作用等于/**,配置方式如下:

    1. <mvc:interceptors>
    2.     
    3.   <bean class="com.itbaizhan.interceptor.MyInterceptor">
    4.   bean>
    5. mvc:interceptors>

    三、拦截器链与执行顺序

    ​

    如果一个URL能够被多个拦截器所拦截,全局拦截器最先执行,其他拦截器根据配置文件中配置的从上到下执行,但是我实操下来发现并不是这样。接下来我来验证一下我的想法,再配置一个拦截器2:

    3.1 拦截器2

    1. package com.example.interceptor;
    2. import org.springframework.web.servlet.HandlerInterceptor;
    3. import org.springframework.web.servlet.ModelAndView;
    4. import javax.servlet.http.HttpServletRequest;
    5. import javax.servlet.http.HttpServletResponse;
    6. import java.util.Scanner;
    7. public class MyInterceptor2 implements HandlerInterceptor {
    8. @Override
    9. public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    10. System.out.print("拦截器2:请求到达Controller前\t");
    11. // 如果return false则无法到达Controller
    12. // 控制台输入决定是否进入Controller
    13. System.out.print("控制台输入决定是否进入Controller: ");
    14. Scanner scanner = new Scanner(System.in);
    15. boolean flag;
    16. flag = scanner.nextBoolean();
    17. return flag;
    18. }
    19. // 跳转JSP前执行,此时可以向Request域添加数据
    20. @Override
    21. public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
    22. System.out.print("拦截器2:跳转JSP前\t");
    23. /*System.out.print("控制台输入决定是否添加数据: ");
    24. Scanner scanner = new Scanner(System.in);
    25. boolean flag;
    26. flag = scanner.nextBoolean();
    27. if(flag)*/
    28. request.setAttribute("age","10");
    29. }
    30. // 跳转JSP后执行,此时已经不能向Request域添加数据
    31. @Override
    32. public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
    33. System.out.println("拦截器2:跳转到JSP后");
    34. request.setAttribute("age",10);
    35. }
    36. }

    这里再配置一个拦截器,为了更能体现拦截器的拦截顺序。 

    3.2 配置拦截器链

    1. <mvc:interceptors>
    2. <mvc:interceptor>
    3. <mvc:mapping path="/m1"/>
    4. <bean class="com.example.interceptor.MyInterceptor"/>
    5. mvc:interceptor>
    6. <mvc:interceptor>
    7. <mvc:mapping path="/m1"/>
    8. <bean class="com.example.interceptor.MyInterceptor2"/>
    9. mvc:interceptor>
    10. <bean class="com.example.interceptor.GlobalInterceptor"/>
    11. mvc:interceptors>

    我们这里测试的拦截器1,2拦截路径都是/m1,我们把全局拦截器放在最后看一下执行顺序是如何的,如果按照上面的说法的话,则应该先提示全局拦截器,再拦截器1,拦截器2的提示信息。接下来我们来看一下实际结果吧。 

    3.3 测试结果

     我们可以看到当访问/m1的时候,首先进入控制器前出现的顺序是拦截器1,然后拦截器2,最后是全局拦截器,然后跳转JSP前的顺序才是全局拦截器、拦截器2,拦截器1,跳转JSP后的也是如此。

    四、拦截器过滤敏感词案例

    接下来我们编写一个拦截器案例,需求如下:
    在系统中,我们需要将所有响应中的一些敏感词替换为 *** ,此时可以使用拦截器达到要求: 

    4.1  编写控制方法

    1. @RequestMapping("/m2")
    2. public String m2(Model model){
    3. model.addAttribute("name","大笨蛋");
    4. return "result";
    5. }

    4.2 创建敏感词拦截器

    1. package com.example.interceptor;
    2. import org.springframework.web.servlet.HandlerInterceptor;
    3. import org.springframework.web.servlet.ModelAndView;
    4. import javax.servlet.http.HttpServletRequest;
    5. import javax.servlet.http.HttpServletResponse;
    6. import java.util.Map;
    7. import java.util.Set;
    8. public class SensitiveWordInterceptor implements HandlerInterceptor {
    9. @Override
    10. public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    11. return true;
    12. }
    13. @Override
    14. public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
    15. // 敏感词列表
    16. String[] sensitiveWords = {"坏人","暴力","笨蛋"};
    17. // model中所有数据
    18. if(modelAndView!=null) {
    19. Map model = modelAndView.getModel();
    20. Set> entries = model.entrySet();
    21. // 遍历model
    22. for (Map.Entry entry : entries) {
    23. String key = entry.getKey();
    24. String value = entry.getValue().toString();
    25. // 将model值和敏感词列表遍历比对
    26. for (String sensitiveWord : sensitiveWords) {
    27. // 如果model包含敏感词,则替换
    28. if (value.contains(sensitiveWord)) {
    29. String newStr = value.replaceAll(sensitiveWord, "***");
    30. model.put(key, newStr);
    31. }
    32. }
    33. }
    34. }
    35. }
    36. @Override
    37. public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
    38. }
    39. }

    4.3 配置拦截器

    1. <mvc:interceptor>
    2. <mvc:mapping path="/**"/>
    3. <bean class="com.example.interceptor.SensitiveWordInterceptor"/>
    4. mvc:interceptor>

    4.4 测试结果

    OK,我们可以发现笨蛋确实是被换成了***。 

    五、跨域请求

    5.1 同源策略

    同源策略是浏览器的一个安全功能。同源,指的是两个URL的协议,域名,端口相同。浏览器出于安全方面的考虑,不同源的客户端脚本在没有明确授权的情况下,不能读写对方资源。
    哪些不受同源策略限制:

    1. 页面中的 跳转、表单提交不会受到同源策略限制的。
    2. 静态资源引入也不会受到同源策略限制。如嵌入到页面中的