jsonp跨域資源引起CORB

weixin_33766168發表於2019-02-26

一、jsonp的使用

jsonp是實現跨域請求資料的一種方式,解決了由於瀏覽器同源策略帶來的安全限制;雖然瀏覽器有同源策略的限制,但對於一些特殊的dom元素卻可引用非同源資源,例如<img src=""/> <script src=""/>等,下面結合例子說明:

  • jquery直接發起ajax呼叫

服務端程式碼

@RequestMapping(value = "/load/data")
public void loadData2(@RequestParam("callback") String callback,
                     HttpServletResponse response) throws IOException {
    Map<String, String> data = new HashMap<>();
    data.put("name", "xudj");
    data.put("age", "18");
    // 轉json
    String jsonData = JSON.toJSONString(data);

    //用回撥函式名稱包裹返回資料
    String result = callback + "(" + jsonData + ")";
    response.getWriter().write(result);
}

客戶端程式碼

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>跨域測試</title>
    <script src="https://code.jquery.com/jquery-3.0.0.min.js"></script>
    <script>
        $(document).ready(function () {
            $("#btn").click(function () {
                $.ajax({
                    url: 'http://localhost:8080/load/data',
                    type: 'GET',
                    success: function (data) {
                        $(text).val(data);
                    }
                });
            });
        });
    </script>
</head>
<body>
<input id="btn" type="button" value="跨域請求資料" />
<textarea id="text" style="width: 200px; height: 100px;"></textarea>
</body>
</html>

呼叫結果
cors跨域
如上,當在localhost:9090站點訪問localhost:8080的介面資源時,出現跨域錯誤。

如錯誤提示,可在伺服器端程式碼中設定響應頭“Access-Control-Allow-Origin”實現允許跨域
  • script解決跨域問題

服務端程式碼
如上不變

客戶端程式碼

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>script解決跨域</title>
    <script src="https://code.jquery.com/jquery-3.0.0.min.js"></script>
    <script>
        //回撥函式
        function showData (result) {
            //json物件轉成字串
            var data = JSON.stringify(result);
            $("#text").val(data);
        }
        $(document).ready(function () {
            $("#btn").click(function () {
                // 向頭部輸入一個指令碼,該指令碼發起一個跨域請求
                $("head").append("<script src='http://localhost:8080/load/data?callback=showData'><\/script>");
            });
        });
    </script>
</head>
<body>
<input id="btn" type="button" value="跨域請求資料" />
<textarea id="text" style="width: 200px; height: 100px;"></textarea>
</body>
</html>

呼叫結果
script解決cprs

  • jsonp解決跨域

服務端程式碼
如上不變

客戶端程式碼

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>jsonp解決跨域</title>
    <script src="https://code.jquery.com/jquery-3.0.0.min.js"></script>
    <script>
        // 回掉函式,預設callback=jQuery30004159376653216822_1550582355513
        function showData(data) {
            console.info("Get Into showData");
            // json物件轉成字串
            var result = JSON.stringify(data);
            $("#text").val(result);
        }
        // 呼叫
        $(document).ready(function () {
            $("#btn").click(function () {
                $.ajax({
                    url: "http://localhost:8080/load/data",
                    type: "GET",
                    dataType: "jsonp", //指定伺服器返回的資料型別
                    jsonpCallback: "showData",  // 指定回撥函式名稱或直接使用回掉函式success
                    jsonp: "callback",   // 預設callback
                    success: function (data) {
                        console.info("Get Into success");
                        // json物件轉成字串
                        // var result = JSON.stringify(data);
                        // $("#text").val(result);
                    }
                });
            });
        });
    </script>
</head>
<body>
<input id="btn" type="button" value="跨域請求資料"/>
<textarea id="text" style="width: 200px; height: 100px;"></textarea>
</body>
</html>

呼叫結果
jsonp解決跨域

通過指定ajax的dataType為“jsonp”,jsonp指定服務端返回jsonp格式資料;請求會自動帶上引數callback=?

二、CORB問題的由來

當服務端程式碼中新增安全響應頭時:
服務端程式碼

@RequestMapping(value = "/load/data")
    public void loadData2(@RequestParam("callback") String callback,
                         HttpServletResponse response) throws IOException {
        // 安全響應頭
        response.addHeader("X-Content-Type-Options", "nosniff");
        response.setContentType("text/html;charset=UTF-8");

        Map<String, String> data = new HashMap<>();
        data.put("name", "xudj");
        data.put("age", "18");
        // 轉json
        String jsonData = JSON.toJSONString(data);

        //用回撥函式名稱包裹返回資料
        String result = callback + "(" + jsonData + ")";
        response.getWriter().write(result);
    }
如上所示,程式碼中多出
// 安全響應頭
response.addHeader("X-Content-Type-Options", "nosniff");
response.setContentType("text/html;charset=UTF-8");

導致使用jsonp解決跨域的請求出現如下錯誤:
jsonp出現CORB

如上,如果服務端程式碼沒有指定ContentType時,則出現如下錯誤:

jsonp拒絕執行script
以上均是由response.addHeader("X-Content-Type-Options", "nosniff");導致的瀏覽器執行script時通過對MIME型別檢測過濾掉不安全的檔案或請求。

三、原因分析

CORB(Cross-Origin Read Blocking):瀏覽器在載入可以跨域資源時,在將資源載入頁面時對其進行識別與攔截等一系列處理。
X-Content-Type-Options(:nosniff):相當於一個提示標誌,被伺服器用來提示客戶端須遵循在Content-Type首部中對MIME型別的設定,不能對其進行修改。從而禁用了客戶端(瀏覽器)的MIME型別嗅探行為(即把不可執行的MIME型別轉變為可執行的MIME型別)。指定值為nosniff時,會拒絕以下兩種請求:
  • 請求型別:style,MIME型別不是“text/css”
  • 請求型別:script,MIME型別不是“Javascript型別”,Javascript型別有text/javascript、application/javascript、application/x-javascript等

所以,當服務端出現response.addHeader("X-Content-Type-Options", "nosniff");安全相應頭,且未指定Content-Type為Javascript型別型別時,jsonp請求跨域資源時變出現如上CORB或拒絕解析的問題。

四、解決辦法

根據第三步問題原因的分析中可知,修改方法有如下兩種:

  1. 去除服務端response.addHeader("X-Content-Type-Options", "nosniff");的配置,但可能造成一些安全上的問題,筆者這裡不做擴充套件,有興趣的同學可以留言討論
  2. 服務指定Content-Type為Javascript型別的一種即可
  3. 啟用jsonp,將跨域的資料請求轉到本站伺服器,由本站伺服器去做跨域請求,即跳過瀏覽器同源策略的限制

相關文章