Spring Boot中解决跨域问题

目录

    • 同源策略
    • 跨域问题描述
    • CORS概念
    • 使用示例
    • 解决方案
      • 1.使用@CrossOrigin注解实现细粒度控制
      • 2.全局配置
      • 3.另一种全局配置方法
    • 非简单请求

说到跨域问题,首先来说说浏览器的同源策略

同源策略

同源策略是由 网景通信公司(Netscape) 提出的一个著名的安全策略,它是浏览器最核心也最基本的安全功能,现在所有支持 JavaScript 的浏览器都会使用这个策略。所谓同源是指协议、域名以及端口要相同。

跨域问题描述

在我们Web开发过程中,经常会遇到跨域问题,就是因为浏览器受到同源策略的限制,对于非同源,Cookie、LocalStorage 和 IndexDB 无法读取; DOM 无法获得;AJAX 请求不能发送。

(图片来源)跨域请求的例子,可以看下表:
Spring Boot中解决跨域问题_第1张图片

解决跨域问题的方案有JSONP和CORD等等。因为JSONP只能发GET请求,不支持其他类型请求,且主要被老的浏览器支持等限制问题。所以推荐使用CORS。

CORS概念

CORS(跨域资源共享)(CORS,Cross-origin resource sharing)是一个 W3C 标准,它是一份浏览器技术的规范,它允许浏览器向跨源服务器,发出XMLHttpRequest请求,从而克服了Ajax只能同源使用的限制。

在Spring Boot提供了CORS的解决方案,下面来看看在Spring Boot中如何CORS。

使用示例

创建两个Spring Boot的项目,两个项目配置不同的端口。都添加spring-boot-starter-web依赖。
第一个项目相当于服务提供方,配置控制器,如下:

@RestController
public class HelloController {
     
  @GetMapping("hello")
  public String hello(){
     
    return "hello get";
  }
  @PostMapping("hello")
  public String hello2(){
     
    return "hello post";
  }
}

第二个项目相当于使用服务方,配置端口号为8082,在resourse/static目录下创建一个html文件。内容如下:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
  <script src="https://apps.bdimg.com/libs/jquery/2.1.4/jquery.min.js"></script>
</head>
<body>
<div id="app1"></div>
<div id="app2"></div>
<input type="button" onclick="btnClick()" value="get按钮">
<input type="button" onclick="btnClick2()" value="post按钮">

<script>
  function btnClick() {
     
    $.get('http://localhost:8080/hello', function (msg) {
     
      $("#app1").html(msg);
    });
  }

  function btnClick2() {
     
    $.post('http://localhost:8080/hello', function (msg) {
     
      $("#app2").html(msg);
    });
  }
</script>
</body>
</html>


两个项目启动,然后在浏览器访问第二个项目的html

在这里插入图片描述
点击get按钮后,在控制台发现报错误
Spring Boot中解决跨域问题_第2张图片
这是由于同源策略的限制,导致了请求发送失败。

解决方案

1.使用@CrossOrigin注解实现细粒度控制

直接在需要被跨域访问的方法上加上@CrossOrigin注解,注解中配置请求路径就行了。

@RestController
public class HelloController {
     
  @GetMapping("hello")
  @CrossOrigin(value="http://localhost:8082")
  public String hello(){
     
    return "hello get";
  }
  @PostMapping("hello")
  @CrossOrigin(value="http://localhost:8082")
  public String hello2(){
     
    return "hello post";
  }
}

@CrossOrigin(value="http://localhost:8082")表示接口接受来自http://localhost:8082地址的请求
重启两个项目,浏览器访问:
Spring Boot中解决跨域问题_第3张图片
可以看到浏览器的请求网络控制台的响应头中多了以下信息
Spring Boot中解决跨域问题_第4张图片
上面请求头信息中,添加了一个Origin字段,用来说明本次请求的源(协议+域名+端口号)。然后服务器来决定是否同意这个请求。
如果Origin的指定的源不在服务器中配置的许可范围,响应头就没有Access-Control-Allow-Origin字段。
如果在许可范围内,服务器返回的响应就会多出图中标识的与CORS请求相关的头信息字段。都是以Access-Control-开头。其中Access-Control-Allow-Origin,是必须的。它的值要么是请求时Origin字段的值,要么是一个*,表示接受任意域名的请求。

2.全局配置

当多个方法都需要跨域请求时,则可以通过全局配置来解决CORS,创建一个配置类里面重写addCorsMappings 方法即可。

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
     
  @Override
  public void addCorsMappings(CorsRegistry corsRegistry){
     
    corsRegistry.addMapping("/**")
            .allowedOrigins("http://localhost:8082")
            .allowedMethods("*")
            .allowedHeaders("*");
  }
}

addMapping表示要添加可以被跨域请求的路径,“/**”表示本应用的所有方法都可以被跨域请求;allowedOrigins表示允许访问的url,allowedMethods表示允许的HTTP方法 ,allowedHeaders表示允许的请求头。

3.另一种全局配置方法

这个方法也是通过重写addCorsMappings方法实现的

@Configuration
public class WebMvcConfig  {
     
  @Bean
  public WebMvcConfigurer corsConfigurer(){
     
    return new WebMvcConfigurerAdapter() {
     
      @Override
      public void addCorsMappings(CorsRegistry registry) {
     
                    registry.addMapping("/**").allowedOrigins("http://localhost:8082").allowedMethods("*").allowedHeaders("*");
      }
    };

  }
}

非简单请求

上面示例中使用的getpost都属于简单请求。简单请求需要符合以下条件:

  1. 请求方法只能为HEAD、GET、POST
  2. 请求头中无自定义头
  3. Content-Type必须为text/plain、multipart/form-data、application/x-www-form-urlencoded

不符合以上条件的是非简单请求。对于非简单请求,浏览器会默认发送两条请求,第一条为预检请求(OPTION),第二条为AJAX的请求。

担任请求方的项目在页面中增加一个put请求:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
  <script src="https://apps.bdimg.com/libs/jquery/2.1.4/jquery.min.js"></script>
</head>
<body>
<div id="app1"></div>
<div id="app2"></div>
<input type="button" onclick="btnClick()" value="get按钮">
<input type="button" onclick="btnClick2()" value="post按钮">
<input type="button" onclick="btnClick3()" value="put按钮">
<script>
  function btnClick() {
     
    $.get('http://localhost:8080/hello', function (msg) {
     
      $("#app1").html(msg);
    });
  }

  function btnClick2() {
     
    $.post('http://localhost:8080/hello', function (msg) {
     
      $("#app2").html(msg);
    });
  }
  function  btnClick3() {
     
    $.ajax({
     
      url:'http://localhost:8080/hello',
      type:'put',
      success:function (msg) {
     
        $("#app2").html(msg);
      }
    });

  }
</script>
</body>
</html>

担任服务方的项目的控制器增加一个put请求处理方法:

  @PutMapping("hello")
  public String hello3(){
     
    return "hello put";
  }

担任服务方的项目的配置类更改为如下:
这里只添加了maxAge的配置,目的在于将预检命令进行缓存,不需要每次请求都先执行预检请求,以减少服务器性能消耗。

@Configuration
public class WebMvcConfig  implements WebMvcConfigurer{
     
  @Override
  public void addCorsMappings(CorsRegistry corsRegistry){
     
    corsRegistry.addMapping("/**")
            .allowedOrigins("http://localhost:8082")
            .allowedMethods("*")
            .allowedHeaders("*")
             .maxAge(3600);
  }
}

在浏览器中进行测试,可以看到一共发了两次请求:
第一次请求的头信息字段:
Spring Boot中解决跨域问题_第5张图片
可以看到预检请求用的请求方法是OPTIONS,表示这个请求是用来询问的。

第二次请求的头信息字段:
Spring Boot中解决跨域问题_第6张图片
服务器收到预检请求后,检查了OriginAccess-Control-Request-MethodAccess-Control-Request-Headers字段以后,确认允许跨源请求,就可以做出HTTP回应。

HTTP回应中即响应头中Access-Control-Allow-Origin表示接受请求的域。

如果浏览器否定了预检请求,会返回一个正常的HTTP回应,但是没有任何CORS相关的头信息字段。这时,浏览器就会认定,服务器不同意预检请求,因此触发一个错误,被XMLHttpRequest对象的onerror回调函数捕获。

访问结果如下:
Spring Boot中解决跨域问题_第7张图片


更详细地学习CORS: 阮一峰《跨域资源共享 CORS 详解》

你可能感兴趣的:(Spring,Boot,java,spring,spring,boot,intellij,idea)