解决同源策略限制以实现跨域访问的两个方法: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。

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