文章目录
场景
最近,使用vue
+element-ui
开发前端界面,在前端界面和后端后台进行联调的时候,发现接口不通,提示报错:
no 'access-control-allow-origin' header is present on the requested resource
这个报错其实是JS跨域问题,具体情况,请各位读者继续往下看。
环境
软件 | 版本 |
---|---|
JDK | 8 |
spring-boot | 2.1.8.RELEASE |
vue/cli | 4.3.1 |
正文
一、介绍
1、根由
跨域问题其实就是浏览器的同源策略所导致的。以下是从《MDN-浏览器的同源策略》摘抄下来的:
同源策略是一个重要的安全策略,它用于限制一个origin的文档或者它加载的脚本如何能与另一个源的资源进行交互。它能帮助阻隔恶意文档,减少可能被攻击的媒介。 ——摘自 《MDN-浏览器的同源策略》
2、同源的定义
如果两个 URL 的 protocol、port (如果有指定的话)和 host 都相同的话,则这两个 URL 是同源。这个方案也被称为“协议/主机/端口元组”,或者直接是 “元组”。(“元组” 是指一组项目构成的整体,双重/三重/四重/五重/等的通用形式)。 ——摘自 《MDN-浏览器的同源策略》
从上面的定义可以知道,满足同源
的条件主要有以下几点:
- 协议相同
- 域名相同
- 端口相同
只要满足这三点,才算是同源。
3、限制同源的缘由
- 限制不同源的请求
设置了同源策略
,当一个网站访问非同源域的网址的时候,默认不会将cookie
这些信息附加上去,避免了信息的泄露。 - 限制DOM操作
当我们登陆恶意网址的时候,同源策略会限制对应的js
操作document
来获取其他网址的cookie
等敏感信息
4、为什么需要跨域
当我们访问跨域请求的时候,像ajax
请求的时候,请求发出来了,但是响应在浏览器端就会被拦截住。像现在都提倡前后端分离
,所以目前博主的项目是前后端分离
的,前端使用vue
+elemenet-ui
来编写,后端使用spring-boot
来搭建项目。而这两个项目肯定是使用各自的域名和端口的。从同源策略
的定义来看,我们浏览器访问肯定是会受限的。所以需要解决跨域问题。
二、解决方法
因为本项目是使用vue
和spring-boot
来搭建的,所以这里的解决方法主要是针对这两个的。如果有其他需要,请查看文章尾部的参考链接
。而说到这里,我们就要介绍一下 CORS
了。
1、CORS简介
跨域资源共享(CORS) 是一种机制,它使用额外的 HTTP 头来告诉浏览器 让运行在一个 origin (domain) 上的Web应用被准许访问来自不同源服务器上的指定的资源。当一个资源从与该资源本身所在的服务器不同的域、协议或端口请求一个资源时,资源会发起一个跨域 HTTP 请求。 ——摘自《MDN-HTTP访问控制(CORS)》
2、前端设置代理
因为我们是使用vue
来搭建项目,vue-cli
是vue
官方提供的脚手架,为熟悉vue
减少操作的复杂性。我们可以在vue.config.js
里面进行设置,如下:
module.exports = {
devServer: {
port: 8001,
proxy: {
"/api": {
target: "http://localhost:8081"
}
}
}
};
缺陷
只能在开发环境使用,生产环境无法使用。
3、后台设置过滤器
博主的项目是spring-boot
项目,所以可以设置过滤器,浏览器进行预检请求
的时候,返回对应的参数,让其可以进行跨域请求。
i、设置环境变量
在application.yml
文件写入以下内容:
spring:
cors:
allowOrigin:
allowMethods: POST,PUT,GET,OPTIONS,DELETE,PATCH
allowHeaders: Authorization,Content-Type
allowCredentials: false
maxAge: 3600
ii、设置 config类
创建CorsFilterProperties
类,定义以下内容:
@Data
@ConfigurationProperties(prefix = "spring.cors")
public class CorsFilterProperties {
private String allowOrigin;
private String allowMethods;
private String allowHeaders;
private Boolean allowCredentials;
private Long maxAge;
}
iii、设置filter类
创建CorsFilterConfig
类,写入以下内容:
@Configuration
@EnableConfigurationProperties({CorsFilterProperties.class})
public class CorsFilterConfig {
@Autowired
private CorsFilterProperties corsFilterProperties;
@Bean
@Order
public FilterRegistrationBean registerCorsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(corsFilterProperties.getAllowCredentials());
if (StringUtils.isBlank(corsFilterProperties.getAllowOrigin())) {
config.addAllowedOrigin("*");
} else {
String[] arr = corsFilterProperties.getAllowOrigin().split(",");
for (String origin : arr) {
config.addAllowedOrigin(origin);
}
}
if (corsFilterProperties.getAllowHeaders().equals("*")) {
config.addAllowedHeader("*");
} else {
String[] headers = corsFilterProperties.getAllowHeaders().split(",");
for (String header : headers) {
if (StringUtils.isNotBlank(header)) {
config.addAllowedHeader(header);
}
}
}
if (corsFilterProperties.getAllowMethods().equals("*")) {
config.addAllowedMethod("*");
} else {
String[] methods = corsFilterProperties.getAllowMethods().split(",");
for (String method : methods) {
if (StringUtils.isNotBlank(method)) {
config.addAllowedMethod(method);
}
}
}
config.setMaxAge(corsFilterProperties.getMaxAge());
source.registerCorsConfiguration("/**", config);
FilterRegistrationBean bean = new FilterRegistrationBean(new CorsFilter(source));
bean.setOrder(Ordered.HIGHEST_PRECEDENCE);
return bean;
}
}
结果
通过在后端后台添加了cors
的支持,使得前后端交互顺利执行。
总结
在实际开发中,遇到问题并追溯问题发生的缘由,不断解决并理解,对于个人能力的提升是很有好处的。
参考链接
彻底理解浏览器的跨域
Spring 里那么多种 CORS 的配置方式,到底有什么区别
10 种跨域解决方案(附终极方案)
随缘求赞
如果我的文章对大家产生了帮忙,可以在文章底部点个赞或者收藏;
如果有好的讨论,可以留言;
如果想继续查看我以后的文章,可以左上角点击关注;
可以扫描以下二维码,关注我的公众号:枫夜之求索阁,查看我最新的分享!