当前位置:   article > 正文

SpringBoot 解决跨域问题的 5 种方案!_springboot跨域配置

springboot跨域配置

跨域问题指的是不同站点之间,使用 ajax 无法相互调用的问题。跨域问题本质是浏览器的一种保护机制,它的初衷是为了保证用户的安全,防止恶意网站窃取数据。但这个保护机制也带来了新的问题,它的问题是给不同站点之间的正常调用,也带来的阻碍,那怎么解决这个问题呢?接下来我们一起来看。

跨域三种情况

请求时,如果出现了以下情况中的任意一种,那么它就是跨域请求:

  1. 协议不同,如 http 和 https;

  2. 域名不同;

  3. 端口不同。

也就是说,即使域名相同,如果一个使用的是 http,另一个使用的是 https,那么它们也属于跨域访问。常见的跨域问题如下图所示:

跨域问题演示

接下来,我们使用两个 Spring Boot 项目来演示跨域的问题,其中一个是端口号为 8080 的前端项目,另一个端口号为 9090 的后端接口项目。

前端网站

前端项目只需要在 resources 下放两个文件,一个用于发送 ajax 请求的 jquery.js,另一个是 html 前端页面,工程目录如下图所示:

其中前端页面 index.html 的代码如下:

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4.     <meta charset="UTF-8">
  5.     <meta http-equiv="X-UA-Compatible" content="IE=edge">
  6.     <meta name="viewport" content="width=device-width, initial-scale=1.0">
  7.     <title>跨域测试页面</title>
  8.     <script src="js/jquery.min.js"></script>
  9. </head>
  10. <body>
  11. <h1>跨域测试</h1>
  12. <div>
  13.     <input type="button" onclick="mySubmit()" value=" 发送跨域请求 ">
  14. </div>
  15. <script>
  16.     function mySubmit() {
  17.         // 发送跨域请求
  18.         jQuery.ajax({
  19.             url: "http://localhost:9090/test",
  20.             type"POST",
  21.             data: {"name""Java"},
  22.             success: function (result) {
  23.                 alert("返回数据:" + result.data);
  24.             }
  25.         });
  26.     }
  27. </script>
  28. </body>
  29. </html>

后端接口

后端接口项目首先先在 application.properties 配置文件中,设置项目的端口号为 9090,如下所示:

server.port=9090

然后创建一个后端控制器,返回一个 JSON 格式的数据,实现代码如下:

  1. import org.springframework.web.bind.annotation.RequestMapping;
  2. import org.springframework.web.bind.annotation.RestController;
  3. import java.util.HashMap;
  4. @RestController
  5. public class TestController {
  6.     @RequestMapping("/test")
  7.     public HashMap<StringObjecttest() {
  8.         return new HashMap<StringObject>() {{
  9.             put("state"200);
  10.             put("data""success");
  11.             put("msg""");
  12.         }};
  13.     }
  14. }

以上两个项目创建并启动成功之后,使用前端项目访问后端接口,因为端口不一样,所以也属于跨域访问,运行结果如下图所示:

解决跨域问题

在 Spring  Boot 中跨域问题有很多种解决方案,比如以下 5 个:

  1. 使用 @CrossOrigin 注解实现跨域;

  2. 通过配置文件实现跨域;

  3. 通过 CorsFilter 对象实现跨域;

  4. 通过 Response 对象实现跨域;

  5. 通过实现 ResponseBodyAdvice 实现跨域。

当然如果你愿意的话,还可以使用过滤器来实现跨域,但它的实现和第 5 种实现类似,所以本文就不赘述了。

解决方案1:通过注解跨域

使用 @CrossOrigin 注解可以轻松的实现跨域,此注解既可以修饰类,也可以修饰方法。当修饰类时,表示此类中的所有接口都可以跨域;当修饰方法时,表示此方法可以跨域,它的实现如下:

  1. import org.springframework.web.bind.annotation.CrossOrigin;
  2. import org.springframework.web.bind.annotation.RequestMapping;
  3. import org.springframework.web.bind.annotation.RestController;
  4. import java.util.HashMap;
  5. @RestController
  6. @CrossOrigin(origins = "*")
  7. public class TestController {
  8.     @RequestMapping("/test")
  9.     public HashMap<StringObjecttest() {
  10.         return new HashMap<StringObject>() {{
  11.             put("state"200);
  12.             put("data""success");
  13.             put("msg""");
  14.         }};
  15.     }
  16. }

以上代码的执行结果如下图所示:

从上图中可以看出,前端项目访问另一个后端项目成功了,也就说明它解决了跨域问题。优缺点分析此方式虽然虽然实现(跨域)比较简单,但细心的朋友也能发现,使用此方式只能实现局部跨域,当一个项目中存在多个类的话,使用此方式就会比较麻烦(需要给所有类上都添加此注解)。

解决方案2:通过配置文件跨域

接下来我们通过设置配置文件的方式就可以实现全局跨域了,它的实现步骤如下:

  • 创建一个新配置文件;

  • 添加 @Configuration 注解,实现 WebMvcConfigurer 接口;

  • 重写 addCorsMappings 方法,设置允许跨域的代码。

具体实现代码如下:

  1. import org.springframework.context.annotation.Configuration;
  2. import org.springframework.web.servlet.config.annotation.CorsRegistry;
  3. import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
  4. @Configuration // 一定不要忽略此注解
  5. public class CorsConfig implements WebMvcConfigurer {
  6.     @Override
  7.     public void addCorsMappings(CorsRegistry registry) {
  8.         registry.addMapping("/**"// 所有接口
  9.                 .allowCredentials(true// 是否发送 Cookie
  10.                 .allowedOriginPatterns("*"// 支持域
  11.                 .allowedMethods(new String[]{"GET""POST""PUT""DELETE"}) // 支持方法
  12.                 .allowedHeaders("*")
  13.                 .exposedHeaders("*");
  14.     }
  15. }

解决方案3:通过 CorsFilter 跨域

此实现方式和上一种实现方式类似,它也可以实现全局跨域,它的具体实现代码如下:

  1. import org.springframework.context.annotation.Bean;
  2. import org.springframework.context.annotation.Configuration;
  3. import org.springframework.web.cors.CorsConfiguration;
  4. import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
  5. import org.springframework.web.filter.CorsFilter;
  6. @Configuration // 一定不能忽略此注解
  7. public class MyCorsFilter {
  8.     @Bean
  9.     public CorsFilter corsFilter() {
  10.         // 1.创建 CORS 配置对象
  11.         CorsConfiguration config = new CorsConfiguration();
  12.         // 支持域
  13.         config.addAllowedOriginPattern("*");
  14.         // 是否发送 Cookie
  15.         config.setAllowCredentials(true);
  16.         // 支持请求方式
  17.         config.addAllowedMethod("*");
  18.         // 允许的原始请求头部信息
  19.         config.addAllowedHeader("*");
  20.         // 暴露的头部信息
  21.         config.addExposedHeader("*");
  22.         // 2.添加地址映射
  23.         UrlBasedCorsConfigurationSource corsConfigurationSource = new UrlBasedCorsConfigurationSource();
  24.         corsConfigurationSource.registerCorsConfiguration("/**", config);
  25.         // 3.返回 CorsFilter 对象
  26.         return new CorsFilter(corsConfigurationSource);
  27.     }
  28. }

解决方案4:通过 Response 跨域

此方式是解决跨域问题最原始的方式,但它可以支持任意的 Spring Boot 版本(早期的 Spring Boot 版本也是支持的)。但此方式也是局部跨域,它应用的范围最小,设置的是方法级别的跨域,它的具体实现代码如下:

  1. import org.springframework.web.bind.annotation.RequestMapping;
  2. import org.springframework.web.bind.annotation.RestController;
  3. import javax.servlet.http.HttpServletResponse;
  4. import java.util.HashMap;
  5. @RestController
  6. public class TestController {
  7.     @RequestMapping("/test")
  8.     public HashMap<StringObjecttest(HttpServletResponse response) {
  9.         // 设置跨域
  10.         response.setHeader("Access-Control-Allow-Origin""*");
  11.         return new HashMap<StringObject>() {{
  12.             put("state"200);
  13.             put("data""success");
  14.             put("msg""");
  15.         }};
  16.     }
  17. }

解决方案5:ResponseBodyAdvice

通过重写 ResponseBodyAdvice 接口中的 beforeBodyWrite(返回之前重写)方法,我们可以对所有的接口进行跨域设置,它的具体实现代码如下:

  1. import org.springframework.core.MethodParameter;
  2. import org.springframework.http.MediaType;
  3. import org.springframework.http.server.ServerHttpRequest;
  4. import org.springframework.http.server.ServerHttpResponse;
  5. import org.springframework.web.bind.annotation.ControllerAdvice;
  6. import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
  7. @ControllerAdvice
  8. public class ResponseAdvice implements ResponseBodyAdvice {
  9.     /**
  10.      * 内容是否需要重写(通过此方法可以选择性部分控制器和方法进行重写)
  11.      * 返回 true 表示重写
  12.      */
  13.     @Override
  14.     public boolean supports(MethodParameter returnType, Class converterType) {
  15.         return true;
  16.     }
  17.     /**
  18.      * 方法返回之前调用此方法
  19.      */
  20.     @Override
  21.     public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType,
  22.                                   Class selectedConverterType, ServerHttpRequest request,
  23.                                   ServerHttpResponse response) {
  24.         // 设置跨域
  25.         response.getHeaders().set("Access-Control-Allow-Origin""*");
  26.         return body;
  27.     }
  28. }

此实现方式也是全局跨域,它对整个项目中的所有接口有效。

原理分析

为什么通过以上方法设置之后,就可以实现不同项目之间的正常交互呢?这个问题的答案也很简单,我们之前在说跨域时讲到:“跨域问题本质是浏览器的行为,它的初衷是为了保证用户的访问安全,防止恶意网站窃取数据”,那想要解决跨域问题就变得很简单了,只需要告诉浏览器这是一个安全的请求,“我是自己人”就行了,那怎么告诉浏览器这是一个正常的请求呢?只需要在返回头中设置“Access-Control-Allow-Origin”参数即可解决跨域问题,此参数就是用来表示允许跨域访问的原始域名的,当设置为“*”时,表示允许所有站点跨域访问,如下图所示:

所以以上 5 种解决跨域问题的本质都是给响应头中加了一个 Access-Control-Allow-Origin 的响应头而已。

总结

跨域问题的本质是浏览器为了保证用户的一种安全拦截机制,想要解决跨域问题,只需要告诉浏览器“我是自己人,不要拦我”就行。它的常见实现方式有 5 种:通过注解实现局部跨域、通过配置文件实现全局跨域、通过 CorsFilter 对象实现全局跨域、通过 Response 对象实现局部跨域,通过 ResponseBodyAdvice 实现全局跨域。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/不正经/article/detail/629122
推荐阅读
相关标签
  

闽ICP备14008679号