跨域方式知多少

前言

好记性不如烂笔头,所以学完跨域之后,我还是老实结合demo来整理一篇跨域实现方式的详记。当然,因为我现在学习阶段,并没有跨域的实战经历,所以这篇整理,纯粹停留在我对跨域的理解层面。
  等后面接触到更多跨域知识,以及经历项目实战的跨域处理,有更透彻的进阶理解,就再做跨域知识的补充或跨域实战的记录。
  这次说的跨域方式有四种:

1、JSONP
2、 CORS
3、 降域(document.domain)
4、 postMessage

一、为何要跨域——因为“同源策略”

1、什么是跨域
通俗来说,就是两个不同域名的网站的JavaScript脚本的交互。
常见交互有:一方发送请求要获取数据,对方响应并传输数据;操作网站页面的DOM元素等。

2、为何要跨域

现实场景中,肯定有很多时候是需要跨域请求、传输数据的。这些合理的用途会被浏览器默认阻止。

那么重点来了,为什么浏览器会默认阻止跨域操作呢?
  因为所有浏览器都奉行“同源策略”

同源策略:只允许同源的JS脚本(或者说接口)进行交互。不同源的情况下,不能读写对方的任何资源。

举例来说,http://www.jianshu.com这个网址,http://协议,www.jianshu.com是域名,端口号不写的情况下默认是80
  那么,同源需要满足下面三点,任何一点不同就视为不同源:

1、同协议:常见协议有http://、https://、file://(本地文件)、ftp://协议
2、同域名:比如www.example.com/dir2/other.html和www.example.com/dir/page.html
3、同端口号:URL默认不写端口,默认端口就是80。
(注意默认80端口和8080端口是不同的,两者不等同。)

同源策略是浏览器出于信息安全考虑,防止恶意网站窃取用户数据。
  例如用户在登录某一网上银行网站后,又去登录其他恶意网站。如果没有同源策略,恶意网站的后台就可以获取网银网页的用户信息。因为浏览器对提交表单并没有同源策略的限制,假如用户登录网银后忘记退出登录。因为登录密码信息存储在cookie中,恶意网站就可以获取用户登录密码等信息,冒充用户,进行恶意操作。

二、使用Ajax跨域请求失败例子

在讲解跨域实现的方式之前,先来实例演示下,不使用任何跨域方式,只是纯粹ajax技术跨域或同域名请求数据时浏览器会做出的默认举动:
  例子:当前页面显示3个新闻标题,点击“换一组”按钮,从指定接口获取数据,随机生成新的3个新闻标题。

index.html文件的代码:




    
    
    
    ajax获取数据
    


    
  • 随机变换新闻标题1
  • 随机变换新闻标题2
  • 随机变换新闻标题3

在模拟的服务端,返回JSON格式的数据:

app.get('/getNews',function(req,res){
    var news=[
        "输入内容1",
        "输入内容2",
        "输入内容3",
        "输入内容4",
        "输入内容5",
        "输入内容6",
        "输入内容7",
        "输入内容8",
        "输入内容9"
    ]
    var data=[];
    for(var i=0;i<3;i++){
        var index=parseInt(Math.random()*news.length); //随机选择news数组的下标
        data.push(news[index]);
    }
    /*ajax的写法*/
    res.send(data);
    //console.log(data) //随机得到:[ '输入内容1', '输入内容4', '输入内容3' ]
})
1、同源域名登录的情况

设置host文件,让www.bbhuangyh.com和www.aahuangyh.com等于127.0.0.1。然后使用http://bbhuangyh.com:8080/域名登录。此时网页的域名和数据请求接口的域名xhr.open('get','http://bbhuangyh.com:8080/getNews',true)相同。点击“换一组”按钮,就能从接口返回数据,获取替换页面数据,实现效果。

334.JPG

2、不同源域名登录的情况

数据请求接口的域名是'http://bbhuangyh.com:8080/getNews,而现在使用localhost:8080或者http://aahuangyh.com:8080/这个完全不同的域名(即使实际都是指向127.0.0.1)来登录,点击“换一组”。这种情况下就是,不同源请求获取数据,浏览器会进行阻拦。点击“换一组”的操作无效。

数据请求情况
preview没有数据返回
response没有数据返回

浏览器返回的错误提醒:

XMLHttpRequest cannot load http://bbhuangyh.com:8080/getNews. 
No 'Access-Control-Allow-Origin' header is present on the requested resource. 
Origin 'http://localhost:8080' is therefore not allowed access.

三、跨域方法——JSONP

1、JSONP的原理

(1)

在模拟的服务端,改写的代码:

app.get('/getNews',function(req,res){
    var news=[
        "输入内容1",
        "输入内容2",
        "输入内容3",
        "输入内容4",
        "输入内容5",
        "输入内容6",
        "输入内容7",
        "输入内容8",
        "输入内容9"
    ]
    var data=[];
    for(var i=0;i<3;i++){
        var index=parseInt(Math.random()*news.length); //随机选择news数组的下标
        data.push(news[index]);
    }
    var cb=req.query.callback; 
    //这里的callback是自定义的名字,跟随html的url请求里的自定义名称一样就行。
    //优化做法:可以满足普通get方法获取,也可以jsonp方法获取
    if(cb){
        res.send(cb+'(' +JSON.stringify(data) + ')');
          //JSON.stringify(data)转化为JSON格式的字符串
         //console.log(cb+'(' +JSON.stringify(data) + ')')随机得到:appendLi(["输入内容8","输入内容6","输入内容4"])
    }else{
        res.send(data);
    }
})

使用localhost:8080、aahuangyh.com或者file协议打开都能随机返回三条数据做显示,实现跨域

localhost:8080打开

aahuangyh.com打开

四、跨域方法——CORS

前面举了个使用AJAX跨域失败的例子。而现在如果使用CORS的跨域方法的话,前面AJAX写法的demo,就能跨域生效。
  CORS是跨源资源分享(Cross-Origin Resource Sharing)的缩写。“它是W3C标准,是跨源AJAX请求的根本解决方法。”——引用

1、实现方法

CORS跨域方法的重点就在于,在后台代码上写上:res.header(“Accsess-Control-Allow-Origin”,“*”)
  一般,使用ajax跨域请求时,浏览器会在请求头Request headers加上“origin:发起请求的域名”
  在后台代码加上res.header(“Accsess-Control-Allow-Origin”,“*”),这样会让浏览器在后端响应头Response headers加上“Accsess-Control-Allow-Origin:*”*号表示对任何域名发送的请求,都给予回应和答应。

2、兼容性

header('Access-Control-Allow-Origin:*')是html5新增的一项标准功能。我们可以来看看CORS的兼容适用情况:

CORS的兼容情况.JPG

  可以看到,IE11以下是不支持的。,无法兼容低版本的IE浏览器来使用。但胜在CORS用法简单,支持所有类型的HTTP请求。使用普通的XMLHttpRequest对象,用AJAX的写法就能实现跨域。

3、实例

来看实例demo吧:
  还是前面AJAX跨域demo,html部分的写法相同,模拟后台部分的代码添加上这一句:

    app.get('/getNews',function(req,res){
    var news=[
        "AAA",
        "BBB",
        "CCC",
        "DDD",
        "EEE",
        "FFF",
        "GGG",
        "HHH",
        "III"
    ];
    var data=[];
    for(var i=0;i<3;i++){
        var index=parseInt(Math.random()*news.length);
        data.push(news[index]);
    }
    //cors跨域写法
    res.header("Access-Control-Allow-Origin","*");
    res.send(data);
    })
CORS方式下的Response Headers

当然,也可以采用指定写法:res.header(“Accsess-Control-Allow-Origin”,“具体URL域名”),这样浏览器就只会对这个指定的URL域名的请求进行回应。
例如:

res.header("Access-Control-Allow-Origin","http://bbhuangyh.com:8080");

就是指定允许http://bbhuangyh.com:8080来跨域访问xhr.open('get','http://aahuangyh.com:8080/getNews',true);指定的这个接口资源

使用指定的对应域名打开正常获取数据
使用别的域名来打开就会报错
(4)其他

当然,还有其他一些配置。例如允许浏览器携带cookie来访问接口资源,后台要写:Access-Control-Allow-Credentials: true
对应的“Accsess-Control-Allow-Origin:*”就不能是*号。而且前端部分的代码就需要配合写上:

var xhr=new XMLHttpRequest();
xhr.open('get',URL,true); //指定接口获取数据
xhr.withCredentials = true; //允许浏览器携带cookie来访问接口,前端配合要写这句
xhr.send();
xhr.onreadystatechange=handler;

五、跨域方法——降域

(1)适用情况

降域方法的实现是使用document.domain适用情景其实非常有限小众,常见于控制

(2)window.frames[0].document.body.querySelector('#input'),就能够操作到右侧iframe窗口的input。因为本质是同源的。

降域截图.jpg

(3)在左边Input输入值,右边input同步

现在,来修改iframe的src,与页面的URL不同域,就会报错:

不降域的情况下报错

这种情况下,就进行降域:
  在两边的html的script中都加入“document.domain="huangyh.com"”。结果两边的input输入都可以同步,实现页面URL和iframe的src不相同的情况下,也能跨域处理:

降域下的跨域操作
降域下的跨域操作结果.JPG

六、跨域方法——postMessage

(1)实现方式

HTML5中新增了window对象的window.postMessage方法,可以实现不同源的域名之间发送、监听消息。具体:

发送消息:windowObj.postMessage(message, targetOrigin);

  • windowObj:需要接收消息的window对象
  • message:发送的消息
  • targetOrigin:接收消息的window对象所在的域名。可以是*号,也可以指定一个明确的URL

监听消息:使用监听事件message

  • oragin:发送消息过来的域名
  • data:发送的数据
  • source:发送消息的window对象
(2)demo实例

简单说明之后,还是来看例子吧。
  这里还是使用降域的这个例子,实现一样的操作效果。但换成postMessage的方式来做,对原有代码做一些改动:
  a.html中的script部分:


b.html中的script部分:


实际在模拟后台运行后,打开a.html查看效果,结果在页面url和iframe的地址完全不同,也可以实现跨域数据互通:

postMessage跨域效果
跨域实现两个input的数据互递

后又后记:

这篇文断续写了挺久。写的时候,对跨域的认识相比学的时候更清晰了些。常翻常新。

参考文章:

https://segmentfault.com/a/1190000006908944
https://segmentfault.com/a/1190000003642057
http://www.ruanyifeng.com/blog/2016/04/same-origin-policy.html

你可能感兴趣的:(跨域方式知多少)