跨域

142 阅读4分钟

1 同源策略

1.1 定义

  • 源 1、window.origin 或 location.origin 可以得到当前源;

2、源 = 协议 + 域名 + 端口号,三者完全一致,则为同源; 例:baidu.comwww.baidu.com 不同源

  • 同源策略 1、浏览器规定,如果 JS 运行在源 A 里,那么就只能获取源 A 的数据,不能获取源 B 的数据,即不允许跨域。

2、同源策略:不同源的页面之间,不准相互访问数据,请求能发送成功,但是浏览器不会给数据。

3、举例:假设 frank.com/index.html 引用了 cdn.com/1.js ,那么就说 1.js 运行在源 frank.com 里,虽然 1.js 从 cdn.com 下载,但是跟 cdn.com 没有关系。所以 1.js 就只能获取 frank.com 的数据,不能获取 1.frank 或者 qq.com 的数据。这是浏览器故意设计的功能。

1.2 原因

  • 保护用户隐私 1、源 A 里的 JS 和源 B 里的 JS,发的请求几乎没有区别(referer有区别, console.log(response.headers['referer']) ),如果后台开发者没有检查 referer,那么源 B 的 JS 就能偷取源 A 的数据。

  • 为什么可以跨域使用 CSS、JS 和图片等? 因为同源策略限制的是数据访问,我们引用 CSSJS图片的时候,其实并不知道内容,我们只是在引用。

2 CORS

跨源资源共享(CORS)

2.1 什么是简单请求

简单请求不会触发 CORS 预检请求,需满足所有下属条件,才是“简单请求”:

2.2 简单请求

  1. 源 B 在响应头里添加 Access-Control-Allow-Origin: http://源 A 即可。

2.3 复杂请求

  1. 响应 OPTIONS 预检请求,源 B 在响应中添加如下的响应头:
Access-Control-Allow-Origin: https://源 A
Access-Control-Allow-Methods: POST, GET, OPTIONS, PATCH
Access-Control-Allow-Headers: Content-Type
  1. 响应实际请求,如 POST 请求,源 B 在响应中添加Access-Control-Allow-Origin 头。
  2. 如果需要携带身份信息,JS 中需要在 AJAX 里设置xhr.withCredentials = true 。此时,源 B 的上述三个响应头都需要有具体的内容,不能是 *

3 JSONP

3.1 JSONP 是什么?

1、场景:在跨域时(在网页 A 访问 网页 B 的数据),浏览器不支持 CORS ;

2、是什么:当前网页 A 创建一个 script 标签,这个标签去请求网页 B 的 js 文件,js 文件会执行一个回调(在当前网页 A 上调用一个全局函数运行,函数名需要和网页 B 中定义的函数名相同),回调里面有我们需要的数据;回调的名字随机生成(避免回调名字和其它的函数名重合),以 callback 参数传给后台,后台会把这个函数返回给我们并执行;

// 网页 A 的 js 代码 
function jsonp(url){
    return new Promise((resolve,reject)=>{
        const random = 'frankJSONPCallbackName' + Math.random()  // 生成随机的回调函数的名字,避免与其它函数名重复;
        window[random]= (data)=>{
            resolve(data)    //成功,处理回调函数的数据
        }
        const script = document.createElement('script')  //创建 script 标签
        script.src = `${url}?callback=${random}`  //将回调名字传给后台,名字默认为 callback 
        script.onload = ()=>{
            script.remove()  //将生成的 script 标签移除,避免 html 结构臃肿
        }
        script.onerror = ()=>{
            reject()  //失败
        }
        document.body.appendChild(script)
    })
}

jsonp('http://qq.com:8888/friends.js')
    .then((data)=>{
        console.log(data)  
    })
    
// 网页 B 的 JS 代码
window['{{funcName}}']({{data}})  //占位符

//网页 B 的 后台代码

const string1 = fs.readFileSync('./public/friends.json').toString()  //获取网页 B 的数据
const string2 = fs.readFileSync('./public/friends.js').toString()   //获取网页 B 的 js 文件
const data = string2.replace('{{data}}',string1).replace('{{funcName}}',query.callback)  //用数据替换占位符
response.write(data)
response.end()

3、JSONP 与 JSON 没有关系,也可以读取 xml 等其它的数据:

window.xxx = `<xml></xml>`

4、通过 js 去拿另一个网站的数据是合法的,浏览器无法限制;

3.2 优点

1、兼容 IE ;

2、可以跨域;

3.3 缺点

1、由于 JSONP 是 script 标签,只支持 get 请求,不能发送 post 请求;

2、读取不到 ajax 那么精确的状态,不知道状态码是多少,也不知道响应头,只知道成功或失败类型。