视频1 视频21 视频41 视频61 视频文章1 视频文章21 视频文章41 视频文章61 推荐1 推荐3 推荐5 推荐7 推荐9 推荐11 推荐13 推荐15 推荐17 推荐19 推荐21 推荐23 推荐25 推荐27 推荐29 推荐31 推荐33 推荐35 推荐37 推荐39 推荐41 推荐43 推荐45 推荐47 推荐49 关键词1 关键词101 关键词201 关键词301 关键词401 关键词501 关键词601 关键词701 关键词801 关键词901 关键词1001 关键词1101 关键词1201 关键词1301 关键词1401 关键词1501 关键词1601 关键词1701 关键词1801 关键词1901 视频扩展1 视频扩展6 视频扩展11 视频扩展16 文章1 文章201 文章401 文章601 文章801 文章1001 资讯1 资讯501 资讯1001 资讯1501 标签1 标签501 标签1001 关键词1 关键词501 关键词1001 关键词1501 专题2001 知道1 知道21 知道41 知道61 知道81 知道101 知道121 知道141 知道161 知道181 知道201 知道221 知道241 知道261 知道281
问答文章1 问答文章501 问答文章1001 问答文章1501 问答文章2001 问答文章2501 问答文章3001 问答文章3501 问答文章4001 问答文章4501 问答文章5001 问答文章5501 问答文章6001 问答文章6501 问答文章7001 问答文章7501 问答文章8001 问答文章8501 问答文章9001 问答文章9501
利用jsonp与代理服务器方案解决跨域问题
2020-11-27 22:30:21 责编:小采
文档

前言

本文将从实践角度介绍如何使用jsonp和代理服务器方案解决跨域问题,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍吧。

浏览器为了保护用户安全,引入了同源策略,即一个服务器页面无法访问另一个协议、域名、端口不同的服务器数据。当页面需要跨服务器访问另一个服务器的数据时,即产生跨域行为。以豆瓣的公开API(https://api.douban.com/v2/book/1220562)为例,当前我的服务器处于http://127.0.0.1:5000下,豆瓣的服务器很显然跟我的服务器不同源,服务器中的一个页面通过AJAX请求该接口时,浏览器会发出如下警告,并且页面获取数据失败:

在实际开发中,如果遇到这样的跨域问题,可以通过以下办法获得跨域的数据:

  • 异源服务器的响应头部设置Access-Control-Allow-Origin允许跨域行为
  • JSONP
  • 设置自己的代理服务器转发异源的数据
  • 对于第一种设置Access-Control-Allow-Origin的方法,如果在Python Flask搭建的服务器下,可以设置一个简单的修饰器:

    from functools import wraps
    from flask import make_response
    
    
    def allow_cross_domain(fun):
     @wraps(fun)
     def wrapper_fun(*args, **kwargs):
     rst = make_response(fun(*args, **kwargs))
     rst.headers['Access-Control-Allow-Origin'] = '*'
     rst.headers['Access-Control-Allow-Methods'] = 'PUT,GET,POST,DELETE'
     allow_headers = "Referer,Accept,Origin,User-Agent"
     rst.headers['Access-Control-Allow-Headers'] = allow_headers
     return rst
     return wrapper_fun
    
    @app.route('/hosts/')
    @allow_cross_domain
    def domains():
     pass

    如果在express搭建的服务器中,类似的可以加入这样一个中间件:

    //allow custom header and CORS
    app.all('*',function (req, res, next) {
     res.header('Access-Control-Allow-Origin', '*');
     res.header('Access-Control-Allow-Headers', 'Content-Type, Content-Length, Authorization, Accept, X-Requested-With , yourHeaderFeild');
     res.header('Access-Control-Allow-Methods', 'PUT, POST, GET, DELETE, OPTIONS');
    
     if (req.method == 'OPTIONS') {
     res.send(200); /让options请求快速返回/
     }
     else {
     next();
     }
    });

    但是设置Access-Control-Allow-Origin的方法有个致命的缺陷,就是只能在提供接口的服务器上进行添加,如果该服务器不是自己开发的话(例如上面提到的豆瓣公开API),这个方法基本可以忽略,那么留给我们自由发挥的方法就只有JSONP和代理服务器了。网上有关于很多JSONP和代理服务器解决跨域的介绍,但都缺少具体的实践案例,本文将通过具体的实际案例来了解这两个跨域方式的具体实现。

    撰写本文时,我手头上可以直接拿来用的后端方案为Flash搭建的RESTful服务器,前端方案为Vue 1.0 + vue-resource进行Ajax,故下面所述具体的实践操作都在这两个环境上进行,如果你的开发环境和这个有差异也没关系,本文将有最少的逻辑代码来展示跨域的实现原理,其他方案可触类旁通。

    JSONP

    浏览器的同源策略限制的跨域的Ajax请求资源,但是script标签中的资源却可以跨域获取,很常见的就是我们通过script标签引用其他服务器的js:

    <script src="https://code.jquery.com/jquery-2.2.4.min.js"></script>

    JSONP的原理就是利用浏览器对script标签没有同源限制,动态创建script标签,把需要请求的API放在script标签的src不受同源策略限制的特性来获得数据。

    JSONP由回调函数(callback)和返回的数据(response)两部分组成。回调函数(callback)是当script创建src引入资源结束时调用,返回的数据(response)作为回调函数的第一个参数传入,在回调函数里即可保存获得的不同源数据。具体来看例子,我们在页面加载的时候获取数据,请求https://api.douban.com/v2/book/1220562:

    // 在vue中需要将回调函数作为一个全局函数,否则在vue的生命周期中将获取不到这个回调函数
    var d = null;
    function handleResponse(response){
     console.log(response);
     d = response;
    }
    
    compiled: function() {
     var self = this;
     // jsonp
     var script = document.createElement("script"); // 动态创建标签
     script.src = "https://api.douban.com/v2/book/1220562?callback=handleResponse"; // 创建的src就是请求的API,同时需要给这个src加上一个callback的query参数,参数名字就是你的回调函数名字
     document.body.appendChild(script, document.body.firstChild); // 插入新创建的script标签,这里类似Ajax发起请求
     
     // 轮询资源获取是否结束
     var timer = setInterval(function () {
     if (d) {
     console.log('pending')
     clearInterval(timer);
     self.data = d; // 将获取的数据赋值给数据model中
     }
     }, 500);
    }

    此时刷新页面,浏览器不再发出Access-Control-Allow-Origin的跨域错误,输出通过script获取到的数据:

    JSONP的缺点

    JSONP的缺点主要源自他的script引用资源方式,JSONP的缺点如下:

  • JSONP是通过script标签获取资源的,也就是说JSONP注定只能用GET的方式访问资源,GET以外的请求无法做到;
  • JSONP是通过src引用不同源的代码,如果其他域的代码存在恶意代码,那么这将造成严重的网络安全,如果需要跨域的服务器不足以信任,那么必须放弃JSONP;
  • 要确定JSONP请求是否成功,需要启动一个计时器监测数据变动。
  • 针对以上JSONP的缺点,如果需要进行改进,就需要使用代理服务器了。

    代理服务器

    代理服务器解决跨域的思路是利用代理服务器对浏览器页面的请求进行转发,因为同源策略的限制只存在在浏览器中,到了服务器端就没有这个限制了,常用的代理服务器方案有使用反向代理服务器以及服务器内转发,使用反向代理服务器的例子是Nginx的反向代理,通过修改Nginx的配置文件,将指定的不同源域名代理到当前服务器上,浏览器就可以正常访问不同源的资源了。还有个方案是不依赖反向代理服务器,在server端对不同源的API进行转发,本文主要对这种方法进行介绍。

    首先代理服务器需要知道浏览器页面需要请求的API,因此,页面需要把API当做参数传递给代理服务器,形如:/proxy/:api,api参数是完整的API链接,如之前提到的豆瓣公共API:https://api.douban.com/v2/book/1220562。server端对API进行转发,在Python中可以使用requests发起HTTP请求,nodejs可以使用request,server端获得响应后将响应的结果返回给浏览器,具体的实现也很简单,以Flask为例:

    @app.route('/proxy/<path:url>', methods=['GET'])
    def getTasks(url):
     r = requests.get(url) ## 请求转发
     conver_r = eval(bytes.decode(r.content)) ##进行一些类型转化
    
     return json.dumps(conver_r), 200

    在浏览器端发起请求的具体代码为:

    self.$http.get('/proxy/https://api.douban.com/v2/book/1220562').then(function(res) {
     self.data = JSON.parse(res.data)
    });

    此时打开浏览器控制台,可以看到server转发的请求结果,跨域成功。

    与JSONP相比代理服务器的优点

    相比JSONP,使用代理服务器转发不同源API的优点如下:

  • 资源获取是通过server端进行,可以根据需要转发的API选择使用GET以外的HTTP方法进行资源请求;
  • 请求的资源需要经过server端转发到浏览器端,server端可以对资源进行处理,因此可以避免一些直接的恶意代码,比JSONP更安全;
  • 浏览器页面正常使用Ajax请求数据,通过回调可以得知请求是否结束,不再需要使用计时器监测。
  • 代理服务器的缺点

    使用代理服务器的缺点是对不同源资源的转发请求,如果同时多个用户进行跨域请求,因为服务器内部需要进行额外的HTTP请求,那么服务器端的处理压力降会变大,从而导致阻塞等一系列性能问题,如需更好的方案,还是得使用Nginx等反向代理服务器进行端口代理处理。

    总结

    本文从实践的角度介绍了JSONP和代理服务器的处理跨域的做法,并对比了两种方案的优缺点,如果是一些安全要求性不高的场景,可以直接使用JSONP进行跨域请求,如果是需要额外的HTTP请求并且安全性要求较高,跨域请求还是从server端发起为佳,当然还有其他跨域方案,需要读者根据自身的能力和判断去舍取。

    好了,

    下载本文
    显示全文
    专题