欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

解决同源策略限制以实现跨域访问的两个方法:JSONP和CORS

程序员文章站 2024-02-18 16:50:28
...

同源的定义:同源是指,域名,协议,端口相同。

比如:在下表中找出与 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和CORS

但是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错误信息

当我们访问页面时,在浏览器的控制台中成功打印出了获取的数据:

解决同源策略限制以实现跨域访问的两个方法:JSONP和CORS

再查看请求头和响应头:

解决同源策略限制以实现跨域访问的两个方法:JSONP和CORS

这里只是实现CORS简单的用法,其实还有很多其他的操作,比如还可以限制请求的方法(get, post等),可以实现用户认证等等。

因此,不管是从功能性还是安全性来说,都更加推荐使用CORS。