1. 程式人生 > >spring boot 中通過CORS實現跨域

spring boot 中通過CORS實現跨域

一、跨域問題出現的原因 

  出現跨域問題的原因是同源策略。

  同源策略

  主要是三同:同協議、同域名、同埠,

  同源策略目的

  • 保證使用者資訊保安,防止惡意網站竊取資料。同源策略是必須的,否則cookie可以共享。

  同源策略的限制範圍

  • cookie、localstorage、indexdb無法讀取。
  • DOM無法獲取。
  • ajax請求不能傳送。

  規避策略

  1. cookie:設定document.domain共享cookie。
  2. DOM獲取:(父子頁面通訊)H5引入了一個API,這個API為windows物件新增了一個window.postMessage方法,允許跨視窗通訊,無論這兩個視窗是否同源。
  3. window.opener.postMessage(content,origin)
    
    content是訊息的具體內容,origin是協議 + 域名 + 埠
  1. AJAX:
  • JSONP:JSONP是伺服器無客戶端跨源通訊的常用方法。基本思想是網頁通過新增一個<script>元素,向伺服器請求json資料,這種做法不受同源政策的限制,伺服器收到請求後,將資料放在一個指定名字的回撥函式裡面傳回來。(只能發GET請求)
  • WebSocket:WebSocket是一種通訊協議。使用ws://(非加密)和wss://(加密)作為協議字首。該協議不實行同源政策,只要伺服器支援,就可以通過它進行跨源通訊。
  • CORS:詳解如下。

  跨域資源共享(corss-origin resource sharing):CORS需要瀏覽器和伺服器同時支援。目前所有瀏覽器都支援該功能,IE瀏覽器不能低於IE10。整個CORS通訊過程,都是瀏覽器自動完成,不需要使用者參與。對於開發者來說,CORS通訊與同源的AJAX通訊沒有差別,程式碼完全一樣。瀏覽器一旦發現AJAX請求跨源,就會自動新增一些附加的頭資訊,有時還會多出一次附加的請求,但使用者不會有感覺。因此,實現CORS通訊的關鍵是伺服器。只要伺服器實現了CORS介面,就可以跨源通訊。

  兩種請求

  • 瀏覽器將CORS請求分成兩類:簡單請求(simple request)和非簡單請求(not-so-simple request)。

  CORS請求

  1. 簡單請求

  • 對於簡單請求,瀏覽器直接發出CORS請求。具體來說,就是在Header中增加一個Origin欄位。如果瀏覽器發現跨源AJAX請求是簡單請求,就自動在頭資訊之中,新增一個Origin欄位。
GET /cors HTTP/1.1      
Origin: http://localhost:8081     //瀏覽器新增欄位,說明本次請求來自哪個源(協議+域名+埠)。
Host: 119.23.214.114
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0...
  • 如果Origin指定的源在不在後端的許可白名單範圍內,伺服器會返回一個正常的http迴應。瀏覽器接收後發現,這個response的Header沒有包含Access-Control-Allow-Origin欄位,就知道出錯了,從而丟擲一個錯誤,被XMLHttpRequestonerror回撥函式捕獲。這種錯誤無法通過狀態碼識別,因此HTTP response的狀態碼有可能是200。

  2. 非簡單請求

  • 非簡單請求是那種對伺服器有特殊要求的請求,比如請求方法是PUTDELETE,或者Content-Type欄位的型別是application/json
  • 非簡單請求的CORS請求,會在正式通訊之前,增加一次HTTP查詢請求,稱為"預檢"請求(preflight)。瀏覽器先詢問伺服器,當前網頁所在的域名是否在伺服器的許可名單之中,以及可以使用哪些HTTP動詞和頭資訊欄位。只有得到肯定答覆,瀏覽器才會發出正式的XMLHttpRequest請求,否則就報錯。
  以上內容連結:https://www.jianshu.com/p/413a2f11828d

二、spring boot 通過CORS解決跨域問題

  先建立一個專案server作為伺服器端,預設埠為8080,並在服務端提供兩個介面:

//方便client分別傳送不同型別請求的時候可以 觀察一下簡單請求和非簡單請求的區別
@RestController public class HelloController { @GetMapping("gethello") //get 型別public String getHello(){ return "get hello"; } @PutMapping("puthello") //put型別 public String putHello(){ return "put hello"; } }

  再建立一個專案client作為請求端,並設定請求端的埠為8081:

server.port=8081

在請求端的resources/static目錄下增加本次訪問所需要的頁面index.html,記得同時自己找一個jquery.min.js 也放到static目錄下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="jquery.min.js"></script>
</head>
<body>
<div id="div"></div>
<input type="button" value="獲取" onclick="gethello()"> //用於測試簡單請求
<input type="button" value="更新" onclick="puthello()"> //使用者測試非簡單請求
<script>
    function gethello() {
        $.get('http://localhost:8080/gethello',function (msg) {
            $("#div").html(msg); //將呼叫介面返回值在頁面上顯示
        });
    }
    function puthello() {
       $.ajax({
           type:'put',
           url:'http://localhost:8080/puthello',
           success:function (msg) {
               $("#div").html(msg);
           }
       })
    }
</script>
</body>
</html><!DOCTYPE html>

    接下來看看跨域失敗的的時候:首先啟動server端,再啟動client端。在瀏覽器中訪問:http://localhost:8081/index.html,點選獲取按鈕:

  然後我們在服務端中通過CORS解決跨域的問題,有兩種解決辦法。

  1.在介面類或者方法上面新增 @CrossOrigin註解

   @GetMapping("gethello")
    @CrossOrigin(value = "http://localhost:8081") //指定可以訪問的白名單
    public String getHello(){
        return "get hello";
    }

  2.實現WebMvcConfigurer介面重寫addCorsMappings方法


@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
    @Override
    public void addCorsMappings(CorsRegistry registry) {
       registry.addMapping("/**").allowedOrigins("http://localhost:8081")
                .allowedMethods("*")
                .allowedHeaders("*")
          .maxAge(30000);
} }

   解決了跨域問題後,重啟服務端:再進行獲取和更新操作:

  1.獲取操作 成功

  

  2.更新操作

  正如前面所說的非簡單請求的CORS請求,會在正式通訊之前,增加一次HTTP查詢請求,稱為"預檢"請求(preflight)。瀏覽器先詢問伺服器,當前網頁所在的域名是否在伺服器的許可名單之中,以及可以使用哪些HTTP動詞和頭資訊欄位。只有得到肯定答覆,瀏覽器才會發出正式的XMLHttpRequest請求,否則就報