SpringMVC解決跨域問題

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

問題css

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

報錯爲前端

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.

這就是典型的跨域問題。java

可是我在瀏覽器裏輸入URL是能夠進行查詢單詞的操做的,有什麼不一樣,即下面兩個問題node

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

通過Google和本身測試jquery

  1. 跨域限制是瀏覽器行爲,不是服務器行爲。 瀏覽器認爲地址欄輸入時安全的,因此不限制認爲是跨域。
  2. 能夠,只要服務器配置爲全部域均可以進行請求,那麼不在服務器運行的HTML就能夠完成http請求。

什麼是跨域問題

同源策略web

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

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 更改端口號 跨域

緣由json

同源政策的目的,是爲了保證用戶信息的安全,防止惡意的網站竊取數據。
設想這樣一種狀況: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. 前端常見跨域解決方案(全)
相關文章
相關標籤/搜索