您当前的位置:首页 > 文章 > Spring MVC拦截器和跨域请求

Spring MVC拦截器和跨域请求

作者:会洗碗的CV工程师 时间:2023-08-08 阅读数:320 人阅读

一、拦截器简介

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

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

二、拦截器使用

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

2.1 控制器方法

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";
    }
}

	

2.2 编写拦截器类

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

  1. preHandle:请求到达Controller前执行的方法,返回值为true通过拦截器,返回值为false被拦截器拦截。
  2. postHandle:跳转到JSP前执行的方法
  3. 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后都有对应的提示。 

2.3 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后才添加注定是没有意义的。

2.4 配置拦截器

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

<!-- 配置拦截器-->
<mvc:interceptors> 
  <mvc:interceptor>    
    <!-- 配置拦截器的作用路径-->   
    <mvc:mapping path="/**"/>    
    <!-- 拦截器对象 -->   
    <bean class="com.itbaizhan.interceptor.MyInterceptor"/> 
  </mvc:interceptor>
</mvc:interceptors>
	

2.5 测试结果

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


2.6 全局拦截器

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

<!-- 配置拦截器 -->
<mvc:interceptors> 
  <!-- 全局拦截器 -->  
  <bean class="com.itbaizhan.interceptor.MyInterceptor">
  </bean>
</mvc:interceptors>
	

三、拦截器链与执行顺序

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

3.1 拦截器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);
    }
}
	

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

3.2 配置拦截器链

    <!-- 配置拦截器-->
    <mvc:interceptors>
        <!-- 拦截器1 -->
        <mvc:interceptor>
            <!-- 配置拦截器的作用路径(没有该作用路径标签则是全局拦截器) -->
            <mvc:mapping path="/m1"/>
            <!-- 拦截器对象 -->
            <bean class="com.example.interceptor.MyInterceptor"/>
        </mvc:interceptor>
        <!-- 拦截器2 -->
        <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的提示信息。接下来我们来看一下实际结果吧。 

3.3 测试结果

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

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

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

4.1  编写控制方法

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

4.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.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<String, Object> model = modelAndView.getModel();
            Set<Map.Entry<String, Object>> entries = model.entrySet();
 
            // 遍历model
            for (Map.Entry<String, Object> 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 {
    }
}
	

4.3 配置拦截器

    <!-- 敏感词拦截器 -->
    <mvc:interceptor>
        <mvc:mapping path="/**"/>
        <bean class="com.example.interceptor.SensitiveWordInterceptor"/>
    </mvc:interceptor>
	

4.4 测试结果

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

五、跨域请求

5.1 同源策略

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

  1. 页面中的 <a> 跳转、表单提交不会受到同源策略限制的。
  2. 静态资源引入也不会受到同源策略限制。如嵌入到页面中的 <script src=""> , <img src=""> ,<link href=""> 等。

最容易收到同源策略影响的就是Ajax请求。

5.2 跨域请求

当请求URL的协议、域名、端口三者中任意一个与当前页面URL不同时即为跨域。浏览器执行JavaScript脚本时,会检查当前请求是否同源,如果不是同源资源,就不会被执行。

当前页面URL  被请求页面URL 是否跨域 原因
https://www.csdn.net/ https://www.csdn.net/index.html
https://www.csdn.net/ http://www.csdn.net/index.html 跨域 协议不同
http://www.csdn.com/ http://www.baidu.com/ 跨域 主域名不同
http://csdn.csdn.net/ http://www.csdn.net/ 跨域 子域名不同
http://www.csdn.net:8080 http://www.csdn.net:8081 跨域 端口号不同

5.3 控制器接收跨域请求

SpringMVC提供了注解@CrossOrigin解决跨域问题。用法如下:

控制器方法

    @RequestMapping("/m3")
    @ResponseBody
    @CrossOrigin("http://localhost:8080")
    public String m3(){
        System.out.println("测试跨域请求");
        return "success";
    }
	

编写JSP页面

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>跨域请求</title>
    <script src="js/jquery-2.1.1.min.js"></script>
    <script>
        $(function (){
            $("#btn").click(function (){
                $.get("http://localhost:8080/m3",function (data){
                    console.log(data);
                })
                /*$.get("http://127.0.0.1:8080/m3",function (data){
                    console.log(data);
                })*/
            })
        })
    </script>
</head>
<body>
    <button id="btn" >异步请求</button>
</body>
</html>
	

测试结果 

当注释掉跨域注解时,运行是这样的。因为是没有跨域,但是当我们使用127.0.0.1时就会报错。

使用127.0.0.1时,且没有添加注解时的运行结果是这样的:可以看到这时就不能成功success了

 

当添加到注解后,无论是8080还是127.0.0.1都能够成功success了

往期专栏&文章相关导读 

     大家如果对于本期内容有什么不了解的话也可以去看看往期的内容,下面列出了博主往期精心制作的Maven,Mybatis等专栏系列文章,走过路过不要错过哎!如果对您有所帮助的话就点点赞,收藏一下啪。其中Spring专栏有些正在更,所以无法查看,但是当博主全部更完之后就可以看啦。

1. Maven系列专栏文章

2. Mybatis系列专栏文章

3. Spring系列专栏文章

4. Spring MVC系列专栏文章   

  

本站大部分文章、数据、图片均来自互联网,一切版权均归源网站或源作者所有。

如果侵犯了您的权益请来信告知我们删除。邮箱:1451803763@qq.com

标签:应用