SpringMVC解決跨域問題


有個朋友在寫扇貝插件的時候遇到了跨域問題。
於是我對解決跨域問題的方式進行了一番探討。

問題

API:查詢單詞
URLhttps://api.shanbay.com/bdc/search/?word={word}
請求方式: GET
參數: {word}, 必須,要查詢的單詞

報錯為

XMLHttpRequest cannot load http://localhost/home/saveCandidate. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'null' is therefore not allowed access. The response had HTTP status code 404.

這就是典型的跨域問題。

但是我在瀏覽器里輸入URL是可以進行查詢單詞的操作的,有什么不同,即下面兩個問題

  1. 為什么在瀏覽器地址欄輸入URL不會出現跨域問題。
  2. 不在服務器運行的html是否可以完成一次http請求

經過Google和自己測試

  1. 跨域限制是瀏覽器行為,不是服務器行為。 瀏覽器認為地址欄輸入時安全的,所以不限制認為是跨域。
  2. 可以,只要服務器配置為所有域都可以進行請求,那么不在服務器運行的HTML就可以完成http請求。

什么是跨域問題

同源策略

同源指的是域名(或IP)協議端口都相同,不同源的客戶端腳本(javascript、ActionScript)在沒明確授權的情況下,不能讀寫對方的資源。

URL 解釋 是否跨域
http://www.morethink.cn 原來的URL
http://www.image.morethink.cn 子域名 跨域(cookie也無法訪問)
http://morethink.cn 不加www 跨域
https://www.morethink.cn 更改協議 跨域
http://www.morethink.cn:8080 更改端口號 跨域

原因

同源政策的目的,是為了保證用戶信息的安全,防止惡意的網站竊取數據。
設想這樣一種情況:A網站是一家銀行,用戶登錄以后,又去瀏覽其他網站。如果其他網站可以讀取A網站的Cookie,會發生什么?
很顯然,如果Cookie包含隱私(比如存款總額),這些信息就會泄漏。更可怕的是,Cookie往往用來保存用戶的登錄狀態,如果用戶沒有退出登錄,其他網站就可以冒充用戶,為所欲為。因為瀏覽器同時還規定,提交表單不受同源政策的限制。
由此可見,"同源政策"是必需的,否則 Cookie 可以共享,互聯網就毫無安全可言了。

同源策略限制以下幾種行為:

  1. Cookie、LocalStorage 和 IndexDB 無法讀取
  2. DOM 和 Js對象無法獲得
  3. AJAX 請求不能發送

模擬跨域問題

測試URL為 http://localhost:80/home/allProductions

可以直接在瀏覽器console中執行

var xhr = new XMLHttpRequest();
xhr.open('GET', 'http://localhost:80/home/allProductions',true);
xhr.send();
xhr.onreadystatechange=function() {
    if(xhr.readyState == 4) {
        if(xhr.status == 200) {
          console.log(JSON.parse(xhr.responseText));
        }
    }
}

在任意網站打開控制台,執行此段代碼可以模擬跨域請求。

在知乎控制台打開報錯如下

Mixed Content: The page at 'https://www.zhihu.com/question/26376773' was loaded over HTTPS, but requested an insecure XMLHttpRequest endpoint 'http://localhost/home/allProductions'. This request has been blocked; the content must be served over HTTPS.

因為知乎是https,報錯與普通的http協議不同。

再澄清一下跨域問題:

  1. 並非瀏覽器限制了發起跨站請求,而是跨站請求可以正常發起,但是返回結果被瀏覽器攔截了。最好的例子是CRSF跨站攻擊原理,無論是否跨域,請求已經發送到了后端服務器!
  2. 但是,有些瀏覽器不允許從HTTPS的域跨域訪問HTTP,比如Chrome和Firefox,這些瀏覽器在請求還未發出的時候就會攔截請求,這是一個特例。

在博客園控制台打開報錯如下

XMLHttpRequest cannot load http://localhost/home/allProductions. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://www.cnblogs.com' is therefore not allowed access.

怎么解決跨域問題

解決方案有很多

  1. 通過jsonp跨域
  2. document.domain + iframe跨域
  3. location.hash + iframe
  4. window.name + iframe跨域
  5. postMessage跨域
  6. 跨域資源共享(CORS)
  7. 前端通過Nginx解決跨域問題
  8. nodejs中間件代理跨域
  9. WebSocket協議跨域

這里主要介紹SpringMVC解決跨域問題的方式。

  1. JSONP
  2. CORS
  3. WebSocket

JSONP

可以直接參考Spring MVC 4.1 支持jsonp進行配置你的SpringMVC注解

JSONP 原理

我雖然請求不了json數據,但是我可以請求一個Content-Typeapplication/javascript的JavaScript對象,這樣就可以避免瀏覽器的同源策略。

就是當服務器接受到名為jsonp或者callback的參數時,返回Content-Type: application/javascript的結果,從而避免瀏覽器的同源策略檢測。

在控制台中直接進行測試你的jsonp是否配置成功:

function println(data) {
    console.log(data);
}

var url = "http://localhost:80/home/allProductions?&callback=println";
// 創建script標簽,設置其屬性
var script = document.createElement('script');
script.setAttribute('src', url);
// 把script標簽加入head,此時調用開始
document.getElementsByTagName('head')[0].appendChild(script);

使用JQuery測試你的jsonp是否配置成功,因為控制台不能直接加載JQuery,需要自己建立html文件來進行測試:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="http://cdn.bootcss.com/jquery/1.10.2/jquery.min.js"></script>
    <script type="text/javascript">
        function println(data) {
            console.log(data);
            console.log('print');
        }
      function jsonp_test() {
            $.ajax({
                type: "get",
                url: "http://localhost:80/home/allProductions",
                dataType: "jsonp",
                jsonp: "callback",//傳遞給請求處理程序或頁面的,用以獲得jsonp回調函數名的參數名(一般默認為:callback)
                jsonpCallback: "println", //返回后調用的處理函數
                error: function () { //請求出錯的處理
                    alert("請求出錯");
                }
            });
        }
    </script>
</head>
<body onload="jsonp_test()">
</body>
</html>

CORS

CORS是一個W3C標准,全稱是"跨域資源共享"(Cross-origin resource sharing)。
它允許瀏覽器向跨源服務器,發出XMLHttpRequest請求,從而克服了AJAX只能同源使用的限制。

CORS需要瀏覽器和服務器同時支持。

  1. 所有瀏覽器都支持該功能,IE瀏覽器不能低於IE10。
    整個CORS通信過程,都是瀏覽器自動完成,不需要用戶參與。 對於開發者來說,CORS通信與同源的AJAX通信沒有差別,代碼完全一樣。瀏覽器一旦發現AJAX請求跨源,就會自動添加一些附加的頭信息,有時還會多出一次附加的請求,但用戶不會有感覺。
  2. 實現CORS通信的關鍵是服務器。只要服務器實現了CORS接口,就可以跨源通信。

即CORS與普通請求代碼一樣。

CORS與JSONP相比

  1. JSONP只能實現GET請求,而CORS支持所有類型的HTTP請求。
  2. 使用CORS,開發者可以使用普通的XMLHttpRequest發起請求和獲得數據,比起JSONP有更好的錯誤處理。
  3. JSONP主要被老的瀏覽器支持,它們往往不支持CORS,而絕大多數現代瀏覽器都已經支持了CORS。

@CrossOrigin注解

此注解既可用於方法也可用於類

源碼如下:

@CrossOrigin(origins = "http://www.zhihu.com")
@RequestMapping(value = "/allProductions", method = RequestMethod.GET)
public Result getAllOldProductions() {

}

@CrossOrigin注解既可注解在方法上,也可注解在類上。

完成配置之后

XML全局配置

所有跨域請求都可以訪問

<mvc:cors>
    <mvc:mapping path="/**" />
</mvc:cors>

更加細粒度的配置:

<mvc:cors>

    <mvc:mapping path="/api/**"
        allowed-origins="http://domain1.com, http://domain2.com"
        allowed-methods="GET, PUT"
        allowed-headers="header1, header2, header3"
        exposed-headers="header1, header2" allow-credentials="false"
        max-age="123" />

    <mvc:mapping path="/resources/**"
        allowed-origins="http://domain1.com" />

</mvc:cors>

WebSocket

WebSocket是一種通信協議,使用ws://(非加密)和wss://(加密)作為協議前綴,在2008年誕生,2011年成為國際標准。所有瀏覽器都已經支持了

它的最大特點就是,服務器可以主動向客戶端推送信息,客戶端也可以主動向服務器發送信息,是真正的雙向平等對話,屬於服務器推送技術的一種。

該協議不實行同源政策,只要服務器支持,就可以通過它進行跨源通信

請求頭信息:(多了個 origin)

GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Version: 13
Origin: http://example.com

響應頭:(如果origin在白名單內)

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk=
Sec-WebSocket-Protocol: chat

相比於HTTP/2

HTTP/2只是對HTML、CSS等JS資源的傳輸方式進行了優化,並沒有提供新的JS API,不能用於實時傳輸消息,也無法推送指定的信息。

參考文檔

  1. 跨域
  2. SpringMVC 跨域解決方法
  3. 前端常見跨域解決方案(全)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM