解決同源策略限制以實現跨域訪問的兩個方法:JSONP和CORS

同源的定義:同源是指,域名,協議,端口相同。

比如:在下表中找出與 http://www.aaa.com:8888/abc/ 同源的URL

URL 結果 原因
http://www.bbb.com:8888/bbb/ 不同源 域名不一致
https://www.aaa.com:8888/ccc/ 不同源 協議不一致
http://www.aaa.com:8080/ddd/ 不同源 端口不一致
http://www.aaa.com:8888/eee/ 同源  

同源策略是瀏覽器的一種安全機制,不同源的客戶端腳本在沒有明確授權的情況下,不能讀寫其他源的資源,例如:在http://www.aaa.com下的js腳本不能通過ajax去訪問http://www.bbb.com下的資源。設想一下,如果沒有同源策略的機制,那麼黑客就很容易通過腳本來攻擊目標的網站。

由於同源策略的存在,所以導致了跨域的問題。我們在寫項目的時候常常會需要解決跨域問題,希望去訪問其他站點的資源,這裏簡單的介紹兩種實現跨域訪問的方法:JSONP,CORS。

JSONP

JSONP是一種將JSON傳遞給瀏覽器的方式,JSONP在JSON的數據基礎上進行了JavaScript的封裝,使它看起來像一個JavaScript的函數,比如:callback({"name": "Messi", "age": 12})。爲什麼要返回這種格式呢?因爲我們知道script標籤不屬於同源策略限制的範疇,所以我們可以使用script標籤的src屬性來訪問目標地址,這時就會返回一個如上面那個數據格式一樣的數據:一個js函數,而我們真正想要的卻是這個函數中的參數(JSON數據),這時就可以實現一個callback函數,將返回的JSON數據進行處理(比如渲染到頁面中)

前端頁面:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="http://libs.baidu.com/jquery/1.9.1/jquery.js"></script>
</head>
<body>

</body>
<script>
    function handler(data) {
        console.log(data)
    }
</script>
<script src="http://127.0.0.1:8888/students_jsonp?callback=handler"></script>
</html>

可以看到該頁面加載時就會請求http://127.0.0.1:8888/students_jsonp這個地址,後面的參數callback表示告訴服務器我需要返回一個由handler函數包裹的數據,因爲在該頁面中有一個handler函數可以處理得到的數據。我這裏直接將數據打印到控制檯。

在後端進行封裝(後端使用的是Flask):

@app.route('/students_jsonp/')
def students_jsonp():
    callback = request.args.get('callback')
    return Response(callback + '(' + json.dumps(students) + ')')

當我加載頁面時,就在控制檯得到了想要的數據:

但是JSONP存在很多問題,比如只能使用get方法,不能自定義http首部等,而且由於JSONP其實是繞過同源策略的限制,所以存在安全隱患,容易被黑客利用。

CORS

CORS,即:跨域資源共享(Cross-Origin Resource Sharing),使用CORS後,如果訪問來自不同的源,就可以只允許來自特定的源的訪問,CORS相比與JSONP而言更加安全。

客戶端在進行跨域訪問源A的時候,會攜帶一個名爲Origin的請求參數,該參數的值是當前客戶端所在的源B,源A所在服務端會事先保存有一個允許訪問的源的清單,當服務端接收到來自源B的請求時,會判斷源B是否在清單中,如果在清單中就允許源B的訪問,並在響應頭的Access-Control-Allow-Origin這個參數中放入與請求頭中Origin參數一樣的值,否則不允許訪問,並返回403錯誤。另外,如果是允許所有的源訪問,則將Access-Control-Allow-Origin的值設置爲 * 。

使用了CORS我們就可以用ajax來實現跨域訪問了。

源B下的頁面代碼:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="http://libs.baidu.com/jquery/1.9.1/jquery.js"></script>
</head>
<body>

</body>
<script>
    $.ajax({
        url: "http://127.0.0.1:8888/students_cors/", //請求的服務端地址
        type: "get",
        dataType: "json",
        success: function (data) {
            //成功之後的處理,返回的數據就是 data
            console.log(data)
        },
        error: function () {
            console.log('error'); //錯誤的處理
        }
    });
</script>
</html>

這裏是實現了頁面加載就發出ajax請求,去請求另一個源的數據,將接收到數據打印在控制檯中。

我們再來看看源A後端接口的實現(使用的是Flask):

allow_origin = ['http://127.0.0.1:9999']

@app.route('/students_cors/')
def students_cors():
    origin = request.headers.get('Origin')
    if origin in allow_origin:  # 判斷源B是否在允許訪問的清單中
        resp = Response(json.dumps(students), content_type='application/json')
        resp.headers['Access-Control-Allow-Origin'] = origin
        return resp
    else:
        return Response(status=403)  # 返回403錯誤信息

當我們訪問頁面時,在瀏覽器的控制檯中成功打印出了獲取的數據:

再查看請求頭和響應頭:

這裏只是實現CORS簡單的用法,其實還有很多其他的操作,比如還可以限制請求的方法(get, post等),可以實現用戶認證等等。

因此,不管是從功能性還是安全性來說,都更加推薦使用CORS。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章