vue學習系列:JS跨域問題的解決

場景

最近,使用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-瀏覽器的同源策略

從上面的定義可以知道,滿足同源的條件主要有以下幾點:

  1. 協議相同
  2. 域名相同
  3. 端口相同
    只要滿足這三點,纔算是同源。

3、限制同源的緣由

  1. 限制不同源的請求
    設置了同源策略,當一個網站訪問非同源域的網址的時候,默認不會將cookie這些信息附加上去,避免了信息的泄露。
  2. 限制DOM操作
    當我們登陸惡意網址的時候,同源策略會限制對應的js操作document來獲取其他網址的cookie等敏感信息

4、爲什麼需要跨域

當我們訪問跨域請求的時候,像ajax請求的時候,請求發出來了,但是響應在瀏覽器端就會被攔截住。像現在都提倡前後端分離,所以目前博主的項目是前後端分離的,前端使用vue+elemenet-ui來編寫,後端使用spring-boot來搭建項目。而這兩個項目肯定是使用各自的域名和端口的。從同源策略的定義來看,我們瀏覽器訪問肯定是會受限的。所以需要解決跨域問題。

二、解決方法

因爲本項目是使用vuespring-boot來搭建的,所以這裏的解決方法主要是針對這兩個的。如果有其他需要,請查看文章尾部的參考鏈接。而說到這裏,我們就要介紹一下 CORS了。

1、CORS簡介

跨域資源共享(CORS) 是一種機制,它使用額外的 HTTP 頭來告訴瀏覽器 讓運行在一個 origin (domain) 上的Web應用被准許訪問來自不同源服務器上的指定的資源。當一個資源從與該資源本身所在的服務器不同的域、協議或端口請求一個資源時,資源會發起一個跨域 HTTP 請求。 ——摘自《MDN-HTTP訪問控制(CORS)

2、前端設置代理

因爲我們是使用vue來搭建項目,vue-clivue官方提供的腳手架,爲熟悉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 種跨域解決方案(附終極方案)

隨緣求贊

如果我的文章對大家產生了幫忙,可以在文章底部點個贊或者收藏;
如果有好的討論,可以留言;
如果想繼續查看我以後的文章,可以左上角點擊關注
拜拜

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章