天天看點

跨域問題——知其然知其是以然一、跨域原因二、解決思路三、解決方法

最近做項目前後端分離才第一次遇到跨域問題,作為渣渣,當然是虛心學習網絡上一些前輩的經驗,以下是自己的總結。哪個地方有錯,還望告知,定虛心請教。

文章内容:

  • 跨域原因
  • 解決思路
  • 解決方法

一、跨域原因

現在的web項目基本是前後端分離,前端調用後端接口,如果前後端代碼不屬于同一個域(同一域名同一端口号),就會産生跨域問題。

常見的跨域問題的報錯提示是:

Failed to load “被調用方域名”: No ‘Access-Control-Allow-Origin’ header is present on the requested resource. Origin ‘調用方域名’ is therefore not allowed access.

跨域問題——知其然知其是以然一、跨域原因二、解決思路三、解決方法

那麼,為什麼會發生AJAX跨域問題?

  • 浏覽器限制
  • 請求跨域通路不同域
  • XHR(XMLHttpRequest)請求

當上述三個條件同時滿足時,就觸發了跨域問題啦~

二、解決思路

跨域問題——知其然知其是以然一、跨域原因二、解決思路三、解決方法

三、解決方法

1.去掉浏覽器限制(以下用chrome做示範)

這種方法并不具有可行性,你沒辦法讓浏覽器都這樣或者每個使用者自己實作。

啟動chrome時加

--disable-web-security

禁止它做跨域校驗。

跨域問題——知其然知其是以然一、跨域原因二、解決思路三、解決方法

2.使用JSONP解決

首先來了解一個JSOP是什麼?

JSONP是一個非官方協定,約定發送請求的參數中如果包含指定的參數,預設為callback.即JSONP請求,伺服器發現其指定參數時,就會把傳回值由原來的JSON對象改成JS代碼。

JSONP通過動态建立JS在JS中送出請求,用完就銷毀,js代碼的内容是函數調用的形式,它的函數名是callback的值,它的函數的參數是原先json對象。

其JS函數後面帶着時間戳防止被浏覽器緩存。此時Content-Type=application/javascript

使用JSONP時伺服器端代碼需要進行改動(這裡用Java代碼示例)

跨域問題——知其然知其是以然一、跨域原因二、解決思路三、解決方法

JSONP的弊端:

  • 需要改動伺服器端
  • 隻支援GET(因為是動态建立script,script隻能GET方法)
  • 發的不是XHR請求(現在XHR有各種特性用于前端開發)
下面,我們來談談跨域問題主流的兩種解決方式吧

3.被調用方解決

  • Filter解決方案
  • nginx解決方案
  • apache解決方案
  • Spring架構解決方案

1.Filter解決方案

采用這種解決方案之前先來看看下面幾個問題。

浏覽器對于請求是先執行還是先判斷是否跨域?
先執行。

是不是所有請求都是先執行呢?
回答這個問題需要弄明白簡單請求和非簡單請求
浏覽器對于簡單請求,就是先執行後判斷。對于非簡單請求,它會先發一個OPTIONS預檢指令,檢查通過再執行。

那麼簡單請求和非簡單請求有哪些呢?
常見的【簡單請求】:GET、HEAD、POST
    請求header裡面
        無自定義頭
        Content-Type為以下幾種:
            text/plain
            multipart/form-data
            application/x-www-form-urlencoded
常見的【非簡單請求】:
    PUT、DELETE的ajax請求
    發送json格式的ajax請求
    帶自定義頭的ajax請求

浏覽器如何判斷?
浏覽器發現請求是跨域的時候,它會在目前請求的請求頭中增加origins字段,然後等請求響應回來,
浏覽器會檢查響應頭中是否存在允許跨域的資訊,沒有就報錯。

帶Cookie的跨域——Access-Control-Allow-Origin: *是否滿足所有跨域場景?
請看下圖:
           
跨域問題——知其然知其是以然一、跨域原因二、解決思路三、解決方法
答案顯然是否,對于帶Cookie的跨域請求必須明确指定Access-Control-Allow-Origin的域名,
并設定Access-Control-Allow-Credentials為true。
           

最後,來看下Filter解決方案的示例代碼吧。

public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
        throws IOException, ServletException {
    HttpServletResponse res = (HttpServletResponse) response;

    HttpServletRequest req = (HttpServletRequest) request;

    String origin = req.getHeader("Origin");

    if (!org.springframework.util.StringUtils.isEmpty(origin)) {
        //帶cookie的時候,origin必須是全比對,不能使用*
        res.addHeader("Access-Control-Allow-Origin", origin);           
    }

    // 允許所有請求方法
    res.addHeader("Access-Control-Allow-Methods", "*");

    String headers = req.getHeader("Access-Control-Request-Headers");
    // 支援所有自定義頭
    if (!org.springframework.util.StringUtils.isEmpty(headers)) {
        res.addHeader("Access-Control-Allow-Headers", headers);         
    }

    res.addHeader("Access-Control-Max-Age", "3600");

    // enable cookie
    res.addHeader("Access-Control-Allow-Credentials", "true");

    chain.doFilter(request, response);
}
           

2.nginx解決方案

跨域問題——知其然知其是以然一、跨域原因二、解決思路三、解決方法

3.apache解決方案

跨域問題——知其然知其是以然一、跨域原因二、解決思路三、解決方法

4.Spring架構解決方案

controller類上加@CrossOrigin注解

4.調用方解決——隐藏跨域

調用方解決跨域問題隻有一種方式,就是利用Http伺服器進行隐藏跨域啦,下面給出nginx和apache兩個伺服器隐藏跨域的配置方式

1.反向代理——nginx配置

調用方設定nginx反向代理

跨域問題——知其然知其是以然一、跨域原因二、解決思路三、解決方法
跨域問題——知其然知其是以然一、跨域原因二、解決思路三、解決方法

2.反向代理——Apache配置

調用方設定Apache反向代理

跨域問題——知其然知其是以然一、跨域原因二、解決思路三、解決方法
跨域問題——知其然知其是以然一、跨域原因二、解決思路三、解決方法

繼續閱讀