SpringBoot + Vue前后端分离开发的跨域问题

原文:SpringBoot + Vue前后端分离开发的跨域问题_小灵宝的博客-CSDN博客

一个因跨域问题报错的例子
  我们以一个简单的springboot + vue前后端分离小例子来引入跨域问题,我们要从前端输入一个字符串,然后通过ajax发送请求将这个字符串传给后端,由后端接收并打印出来
  这个例子十分简单,我们直接上代码:
  后端:

@RestController
@RequestMapping("/data")
public class GetAndPrintData {

    @PostMapping("/put/string")
    public void getAndPrintString(@RequestBody StringData stringData) {
        System.out.println(stringData.getStringData());
    }
}
1
2
3
4
5
6
7
8
9
@Data
public class StringData {
    private String stringData;
}
1
2
3
4
  前端:

<template>
  <div>
    <el-form ref="dataForm" :model="dataForm" label-width="80px" class="login-box">
      <h3 class="login-title">来传输数据</h3>
      <el-form-item label="字符串数据" prop="stringData">
        <el-input type="text" placeholder="请输入字符串信息" v-model="dataForm.stringData"/>
      </el-form-item>
      <el-form-item>
        <el-button type="primary" v-on:click="onSubmit()">传输数据</el-button>
      </el-form-item>
    </el-form>
  </div>
</template>

<script>
export default {
  name: "CrosTest",
  data() {
    return {
      dataForm: {
        stringData: ''
      },
    }
  },
  methods: {
    onSubmit() {
      axios({
        method: 'POST',
        url: 'http://localhost:8181/data/put/string',
        data: this.dataForm
      })
    }
  }
}
</script>

<style scoped>

</style>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
  这里要注意的点是前端传的JSON中的key要和后端实体类中的属性对应,后端实体类属性为stringData,前端传的JSON中的key也要为stringData,若为stringdata,则对应不上,后端将接收不到相应数据

  我们启动前后端后,输入字符串并提交,发现前端报错,后端没有接收到数据

  来看看具体的报错信息:
Access to XMLHttpRequest at 'http://localhost:8181/data/put/string' from origin 'http://localhost:8081' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.
  它说我们的http请求被CORS policy给blocked了,那么什么是CORS policy呢?接下来我们就来谈谈跨域问题

跨域问题与浏览器的同源策略
  什么是跨域呢?浏览器从一个域名的网页去请求另一个域名的资源时,域名、端口、协议任一不同,都是跨域。在我们的前后端分离开发中,首先,前后端端口就一定不同,也就是说,前端发请求到后端一定是跨域。与跨域相对的概念就是同源,两个页面地址中的协议,域名,端口号一致,则表示同源。
  浏览器采用了同源策略。同源策略保证了以下3点:
  1、无法读取非同源网页的 Cookie、LocalStorage 和 IndexedDB
  2、无法接触非同源网页的 DOM(HTML DOM 定义了用于 HTML 的一系列标准的对象,以及访问和处理 HTML 文档的标准方法)
  3、无法成功向非同源地址发送 AJAX 请求(可以发送,但浏览器会拒绝接受响应)
  为什么浏览器要采取同源策略呢?主要是为了保证安全,如果没有同源限制存在,浏览器中的cookie等其他数据可以任意读取,不同域下DOM任意操作,ajax任意请求的话,如果浏览了恶意网站那么就会泄漏这些隐私数据,恶意程序也能给别的网站疯狂进行ajax请求。
  我们之前的例子就是因为向非同源地址发送了ajax请求,被浏览器的同源策略拒绝了

如何解决跨域问题
后端来解决
  既然无法成功向非同源地址发送 AJAX 请求,那有没有出现什么方法来解决这个问题呢,毕竟我们是要经常发送跨域请求的。W3C制定了一个标准CORS,全称是"跨域资源共享"(Cross-origin resource sharing)。它允许浏览器向跨源服务器,发出XMLHttpRequest请求,从而克服了AJAX只能同源使用的限制。
  CORS需要浏览器和服务器同时支持,浏览器这端完全不用担心,目前的浏览器一般没有不支持的。整个CORS通信过程,都是浏览器自动完成,不需要用户参与。对于开发者来说,CORS通信与同源的AJAX通信没有差别,代码完全一样。浏览器一旦发现AJAX请求跨源,就会自动添加一些附加的头信息,有时还会多出一次附加的请求,但用户不会有感觉。因此,实现CORS通信的关键是服务器。只要服务器实现了CORS接口,就可以跨源通信。这就是后端的任务了。
  这种方法是跨源 AJAX 请求的根本解决方法。
  因此,我们只要让后端实现CORS接口就好了,CORS接口的具体细节可以参考:跨域资源共享 CORS 详解。

  我们使用@Configuration注解创建一个配置类,该配置类需实现WebMvcConfigurer接口,这里提供两种方式来进行配置:
  方法1:配置类里面使用@Bean标注在构造器方法上用以注入一个CorsFilter对象,此方法适用于配置类需要进行很多配置的场合,推荐使用,下面上代码:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class Configurer implements WebMvcConfigurer {
    @Bean
    public CorsFilter corsFilter() {
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        CorsConfiguration config = new CorsConfiguration();
        config.setAllowCredentials(true);   // 可选字段,表示是否允许发送Cookie,true表示允许
        config.addAllowedOrigin("http://localhost:8081");   // 必填字段,"*"表示接受任意域名的请求
        config.addAllowedHeader("*");   // 可选字段,允许CORS请求额外发送的头信息字段
        config.addAllowedMethod("*");   // 必填字段,允许CORS请求使用的HTTP方法, "*"表示全部方法
        config.setMaxAge(3600L);    // 可选字段,用来指定预检请求的有效期,单位为秒。在有效期间,不用发出另一条预检请求
        source.registerCorsConfiguration("/**", config);    // 必填字段,"/**"表示请求路径是多级,"/*/*"表示请求路径是两级
        return new CorsFilter(source);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
  方法2:配置类重写addCorsMappings方法,若该配置类只负责CORS配置,则可使用此方法:

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class Configurer implements WebMvcConfigurer {

    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")  // 必填字段,"/**"表示请求路径是多级,"/*/*"表示请求路径是两级
                .allowedOriginPatterns("http://localhost:8081")     // 必填字段,"*"表示接受任意域名的请求
                .allowedMethods("GET", "HEAD", "POST", "PUT", "DELETE", "OPTIONS")  // 必填字段,允许CORS请求使用的HTTP方法, "*"表示全部方法
                .allowCredentials(true)     // 可选字段,表示是否允许发送Cookie,true表示允许
                .maxAge(3600)   // 可选字段,用来指定预检请求的有效期,单位为秒。在有效期间,不用发出另一条预检请求
                .allowedHeaders("*");   // 可选字段,允许CORS请求额外发送的头信息字段
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
  这里对上述代码解释一下,Origin字段在CORS中用来说明,本次请求来自哪个源(协议 + 域名 + 端口)。服务器根据这个值,决定是否同意这次请求。我们在配置中设置了allowCredentials(true),表示允许发送Cookie,此时allowedOriginPatterns就不能设为星号,必须指定明确的、与请求网页一致的域名。同时,Cookie依然遵循同源政策,只有用服务器域名设置的Cookie才会上传,其他域名的Cookie并不会上传,且(跨源)原网页代码中的document.cookie也无法读取服务器域名下的Cookie,这也是为了安全考虑,因此我们将允许的OriginPatterns设为前端域名http://localhost:8081。同时由于Cookie的收发前后端都要参与,必须在AJAX请求中打开withCredentials属性,我们要在vue项目的main.js中加上这样一句配置:axios.defaults.withCredentials=true;

  不加上这句配置,即使服务器同意发送Cookie,浏览器也不会发送。或者,服务器要求设置Cookie,浏览器也不会处理。
  下面我们来测试一下:

  成功解决了跨域问题

前端来解决
  前端来解决跨域问题有几种方法:
  1、使用WebSocket通信协议,这里不详解。该协议不实行同源政策,只要服务器支持,就可以通过它进行跨源通信。
  2、中间代理服务转发:在前端服务和后端接口服务之间架设一个中间代理服务,它的地址保持和后端服务器一致,这样,我们就可以通过中间服务做接口转发,在开发环境下解决跨域问题。我们需要vue.config.js中配置代理服务,通过这个代理服务发送请求,由于代理服务和后端端口号相同,这样就不存在跨域的问题了,代码如下:

'use strict'
module.exports = {
    dev: {
        host: 'localhost',
        port: 8081,
        autoOpenBrowser: false,
        overlay: {
            warnings: false,
            errors: true
        },
        // 代理配置
        proxy: {
            // 如果请求地址以/data打头,就触发代理机制
            // http://localhost:8081/data/put/string -> http://localhost:8181/data/put/string
            '/data': {
                target: 'http://localhost:8181',//后端接口地址
                changeOrigin: true,//是否允许更改Origin
                pathRewrite: {
                    '^/data': '/data',//重写,
                }
            }
        },
    },
}
 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值