同源策略/跨域解决方案(CORS、JSON)

492 阅读4分钟

什么是同源策略

同源策略是浏览器对域名之间资源的一种限制方式,这是浏览器所设计的功能(浏览器故意这样做的),为的就是保护用户的隐私,防止用户的数据泄漏。

那么怎样的域名算不同源

baidu.com 和 qq.com
baidu.com 和 www.baidu.com
localhost:8888 和localhost:9999

上面的域名都是不同源的。只要你的域名有一点点不一样都是无法访问的,甚至端口号不同都属于不同源。

实验1 浏览器的同源策略

在本地开启了两个服务器,假设一个是qq.com,一个是黑客的ying.com,其ip地址都是本地的127.0.0.1只是端口号不同。接着ying.com试图访问qq.com源中的friends.json数据文件。

下面是qq.com自己访问friends.json的效果

成功获取到数据并渲染、

下面是黑客ying.com访问qq.com 中的friends.json的效果

报错表示地址之间不同源

**问:**为什么可以跨域使用css,js和图片资源

同源策略限制的是数据的访问,我们引用css、js、图片的时候其实并不知道其内容是什么,我们只是在引用。

如何实现跨域

如果说两个网站都是我的,有没有什么方法能够实现数据共享。

解决方案一 CORS

通过在资源的Response Header上添加Access-Control-Allow-Origin: <允许访问的域名>。就可以实现跨域名资源共享

实验2 使用CORS实现跨域

在服务器qq.com的服务器中设置qq.js文件的响应头

//允许来自 http://ying.com:9999 源的访问
response.setHeader('access-control-allow-origin', 'http://ying.com:9999');

在ying.com中成功获取到了qq.com中的json数据

CORS 在IE 6 7 8 9 中都不兼容

解决方案二 JSONP

为了兼容性IE又出现了JSONP这种技术,JSONP与JSON之间没有关系,只不过一般传输的都是json的数据类型。

基本思想就是利用浏览器没有限制CDN的引用。可以通过将数据方到js文件中然后将script脚本引入网页再适当的处理一下就可以得到其他网站的数据了。这需要资源方提供专门写一个js文件。

实验3 使用JSONP实现跨域

为了兼容IE没有办法使用CORS来实现跨域,为了实现资源共享pp.com提供了一个包含JSON数据的friends.js文件。接着ying.com使用script标签将pp.com的friends.js文件引入自己的网站,从而实现跨域获取数据。

服务器会将json数据插值到friends.js的{{data}}

//server.js
if (path === "/friends.js") {
        response.statusCode = 200;
        response.setHeader('Content-type', 'text/js;charset=utf-8');
        let string = fs.readFileSync('public/friends.js').toString();
        let json = fs.readFileSync('public/friends.json').toString();
        string = string.replace('"{{data}}"', json)
        response.write(string)
        response.end();
    }

将qq.com的friends.js引入到ying.com中,并做一些处理

ying.com中顺利获取到了qq.com中的数据

使用回调

这里的还可以不使用onload事件而换成回调

ying.js文件

//定义函数
window.friends = function(data) {
    console.log('data:');
    console.log(data);
    render(data);
}

qq.com文件

//调用函数
window.friends("{{data}}") //这里的插值"{{data}}"会被服务器改为json数据

JSON存在的问题

  1. 所有网站都可以引用这个js文件
  2. 全局变量的命名空间冲突(如上面例子的window.friends)
  3. 每次使用JSONP请求数据都需要创建一个script标签

基于这些问题下面就来逐个击破

解决方法

  1. 使用referer再阻止一下网站的请求。如果请求头中的referer来判断是否是要给该网站数据
  2. 使用随机数加前缀
  3. 每次用完删除script标签,这样子是不会影响数据获取的

封装JSONP

//prve 为前缀
function jsonp(url, prve) {
    return new Promise((resolve, reject) => {
        const random = prve + Math.random();
        window[random] = function(data) {
            resolve(data);
        }
        const script = document.createElement('script');
        script.src = `${url}?callback=${random}`
        script.onload = () => {
            script.remove();
        }
        script.onerror = () => {
            reject();
        }
        document.head.appendChild(script);
    })
}

JSONP的优缺点

优点
  1. JSONP可以做到不是当前的域名而是其他的域名也可以成功的跨域
  2. JSONP可以兼容IE,或在某种无法使用CORS的情况下成功的跨域

缺点

  1. JSONP 是script标签不能先ajax一样获取到数据返回的状态
  2. 有由于它是script标签所有只能发get请求,也就是说JSONP不支持POST

总结

只有在CORS不支持的情况下才会去使用JSONP 实验代码