文章目錄
場景
最近,使用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 種跨域解決方案(附終極方案)
隨緣求贊
如果我的文章對大家產生了幫忙,可以在文章底部點個贊或者收藏;
如果有好的討論,可以留言;
如果想繼續查看我以後的文章,可以左上角點擊關注