最常用的两种跨域解决方案

同源策略:

是由NetScape提出的著名的安全策略,所有支持javaScript的浏览器都使用这个策略。同源策略限制了一个源中加载文本或脚本与来自其它源中资源的交互方式。

IE特例:

  • 授信范围(Trust Zones):两个相互之间高度互信的域名,如公司域名(corporate domains),不遵守同源策略的限制。

  • 端口:IE未将端口号加入到同源策略的组成部分之中,因此 http://company.com:81/index.htmlhttp://company.com/index.html 属于同源并且不受任何限制。

以上参考自MDN

所谓同源是指:域名、协议、端口都相同。同源策略是浏览器最核心也最基本的安全功能
这是知乎上关于同源策略的一篇文章。。但是浏览器在安全性和实用性上做出了让步,img/script/style/iframe等有src属性的都可以跨域引用资源。

方案一——通过JSONP跨域

jsonp是JSON with padding的简写,看起来与json差不多,但是包含在函数调用中的json,利用动态script元素来使用(具有src属性的如img,iframe,srcipt都不受同源策略的影响)。该协议的一个要点就是允许用户传递一个callback参数给服务端,然后服务端返回数据时会将这个callback参数作为函数名来包裹住JSON数据,这样客户端就可以随意定制自己的函数来自动处理返回数据了。

如果使用jquery,可以在type为get的时候dataType设为jsonp,就可以了。

$.ajax({
        url: 'http://www.qdaily.com/get_user_and_radar.json?winWidth=1280&winHeight=800',
        type: 'get',
        dataType: 'jsonp',
        data: {},
    })
    .done(function(data) {
        console.log(data.status);
    })

然后,很幸运的就可以看到这个:

Paste_Image.png

但是可以看出其实是请求到数据的,只是没有执行我们的callBack函数。

Paste_Image.png

因为前面有提到:服务端返回数据时会将这个callback参数作为函数名来包裹住JSON数据,也就是服务端的返回需要拼接成这样(比如函数名叫做haddleData):

haddleData({"status":true,subscribes:["12","23"]})

另外:jquery的ajax方法可以传入两个参数

jsonp : "callback"//设置这个会替换浏览器发送请求时地址后面自动添加的?callback=xxx中的callback这个字,一般情况下不用传这个参数
jsonpCallback: "success_jsonpCallback"//这个值将用来取代jQuery自动生成的随机函数名,也就是上句话中的'xxx'。

【这儿遗留了一个问题,还请各位解决】如果jsonpCallback不手动设置的话,jquery是会自动生成个随机的字符串的,但是服务器那边的函数名要写什么,回调函数才会正确的执行呢?

看一个运行成功的实例:

$.ajax({
        url: 'http://localhost:8000/remote/remote.js',
        type: 'get',
        dataType: 'jsonp',
        jsonp: 'back',
        jsonpCallback: "haddleData",
        data: {},
    })
    .done(function(data) {
        console.log(data.status);
    })
//这段程序运行在8080端口的

remote.js作为远端其中写了句

haddleData({"status":true,subscribes:["12","23"]})
Paste_Image.png

对于大部分js初学者,大多使用jquery,而对原生不太熟悉。但是须知,ajax和jsonp其实本质上是不同的东西,ajax的核心是通过XmlHttpRequest获取非本页内容,而jsonp的核心则是动态添加<script>标签来调用服务器提供的js脚本,利用脚本下载下来后立即执行的事实实现callback方法的调用。

原生js的jsonp简单实现:

<script>
    function haddleData(data){
        console.log(data.status);
    }
</script>
<script type="text/javascript" src="http://localhost:8000/remote/remote.js"></script>

当然有多个本地函数需要处理的时候,加上回调函数名才是方便的,像这样:

<script type="text/javascript" src="http://localhost:8000/remote/remote.js?callback=haddleData"></script>

加上callback参数,可以使得服务器端根据前端指定的方法名cb动态返回cb(data);而不是都写死handleData(data);

【总结下jsonp】:

  • 优点:
    简单,函数回调在本地处理;
  • 缺点:
    1、安全性(存在注入漏洞,如CSRF,XSS);
    2、如果出现错误,不会像http请求那样有状态码;
    3、只能使用get请求;

方案二——CORS(Cross-Origin Resource Sharing)

这是一个W3C标准(显然比jsonp背景深厚许多),同样需要浏览器和服务器同时支持,但是整个通信过程,都是浏览器自动完成,不需要用户参与,就像平时写Ajax一样(如果使用的是jquery的话)。
下面是原生js实现CORS的代码

    function createCORSRequest(method,url){
        var xhr=new XMLHttpRequest();
        if("withCredentials" in xhr){
            xhr.open(method,url,true);
        }else if(typeof XDomainRequest != "undefined"){//IE10之前的版本使用XDmainRequest支持CORS
            xhr=new XDomainRequest();
            xhr.open(method,url);
        }else{
            xhr=null;
        }
        return xhr;
    }
    var request=createCORSRequest("get","待访问的地址");
    if(request){
        request.onload=function(data){
            //do sth
        };
        request.send();
    }   

适用场景:

承载的信息量大,get形式搞不定,需选用post传输。CORS支持所有类型的传输。

兼容性:

移动端全面支持(除opera mini),PC上IE8+。

CORS思想:

使用自定义的HTTP头部让浏览器与服务器进行沟通,从而决定请求或响应是应该成功还是失败。(请求和响应都不包含cookie)

当然如果设置成下面这样,所有的跨域都可以实现了,但这样毕竟太不安全。
"Access-Control-Allow-Origin:*";//允许任何域向我们的服务器发送请求

一般情况下,浏览器发送一个额外的Origin头部(由浏览器自动生成发送)

Origin:http://localhost:8080//本地网址

然后由服务器发送一个响应表头:Access-Control-Allow-Origin,如果服务器接收该请求,返回值(只能是通配符或单域名。)就和请求值一样。

Access-Control-Allow-Origin:http://localhost:8080

=>CORS方案的重点其实就在于服务器端的配置。

简单请求

  • 只使用 GET, HEAD 或者 POST 请求方法。

如果使用 POST 向服务器端传送数据,则数据类型(Content-Type)只能是 application/x-www-form-urlencoded, multipart/form-data 或 text/plain中的一种。

  • 不会使用自定义请求头(类似于 X-Modified 这种)。

HTTP头部信息不超出以下{Accept,Accept-Language,Content-Language,Last-Event-ID,content-type(只限于上面提到的3种类型)}

对于简单请求,浏览器直接发出CORS请求。浏览器会自动在头信息(Request Headers)中,添加一个Origin 字段,来表明本次请求来自哪个域。

Paste_Image.png

如果这个源不在许可范围内,会报错: No 'Access-Control-Allow-Origin' header is present on the requested resource.

如果Origin指定的域名在许可范围内(必须是跨域了的),Response Headers中会多出几个头信息字段。

Access-Control-Allow-Credentials:true//值为true表示允许发送cookie
Access-Control-Allow-Methods:GET, POST, OPTIONS
Access-Control-Allow-Origin:http://localhost:8080
Access-Control-Max-Age:1728000
withCredentials属性

因为CORS默认不发送cookie和http认证,如果要把Cookie发到服务器,就要指定Access-Control-Allow-Credentials:true;
另外AJAX中也要打开withCredentials属性。

var xhr=new XMLHttpRequest();
xhr.withCredentials=true;

jquery ajax请求参数中加入

xhrFields: {
  withCredentials: true
}

非简单请求

除了上面说的简单请求外都是非简单请求,比如:请求方法是PUT
或DELETE,或者Content-Type字段的类型是application/json,又或者有自定义请求头Access-Control-Request-Headers: X-Custom-Header。

比如,我添加自定义请求头

xhr.setRequestHeader('Some-Custom-Response-Header', 'value');

就会发现连续向同一地址请求了两次,而第一次请求什么值也没拿到

Paste_Image.png

第二次请求

Paste_Image.png

这是因为浏览器发现,这是一个非简单请求,就自动发出一个"预检"请求,要求服务器确认可以这样请求。"预检"请求用的请求方法是OPTIONS,表示这个请求是用来询问的。"预检"请求之后,浏览器球会进行正常CORS请求。

服务器端配置(rails为例)

1、可以参考这个方法:为 RESTful API 配置 CORS 实现跨域请求,写的挺详细的。然而对于rails并不算熟悉的我来说,有gem包,怎么可能放着不用呢~
2、这是Rack CORS 中间件的项目地址,按照文档的说明1分钟就可以基本搞定。

【问题:】

当我请求方式为put时,却出现了404错误,是因为没有为options提供路由么?

rails中使用CORS中的功能和细节还有很多,等到需要时再行挖掘吧,欢迎使用过的各位一起交流问题。

【参考】

http://mp.weixin.qq.com/s?__biz=MjM5MTA1MjAxMQ==&mid=402804565&idx=1&sn=415fc9eab30edcb66227ed5e099f9a66&scene=1&srcid=0413psVAfbsElTaWvrVas6xM#rd
http://www.ruanyifeng.com/blog/2016/04/cors.html
http://www.cnblogs.com/dowinning/archive/2012/04/19/json-jsonp-jquery.html
http://www.cnblogs.com/know/archive/2011/10/09/2204005.html
http://blog.csdn.net/xiaoxian8023/article/details/27817861

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,736评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,167评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,442评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,902评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,302评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,573评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,847评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,562评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,260评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,531评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,021评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,367评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,016评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,068评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,827评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,610评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,514评论 2 269

推荐阅读更多精彩内容