文章回答以下问题:
- 什么是同源策略
- 为什么需要同源策略
- 跨域是什么
- 跨域的方法1:CORS
- 跨域的方法2:JSONP
同源策略
什么是同源策略
最初,同源策略(same-origin policy)的含义是指,A 网页设置的 Cookie,B 网页不能打开,除非这两个网页“同源”。这是浏览器的功能。所谓“同源”指的是“三个相同”:
- 协议相同
- 域名相同
- 端口相同
举例👇
http://www.example.com/dir2/other.html:同源http://example.com/dir/other.html:不同源(域名不同)http://v2.www.example.com/dir/other.html:不同源(域名不同)http://www.example.com:81/dir/other.html:不同源(端口不同)https://www.example.com/dir/page.html:不同源(协议不同)
同源策略的目的
同源政策的目的,是为了保证用户信息的安全,防止恶意的网站窃取数据。
不同网站发送的请求,几乎没有区别(referer有区别),但是只区分referer不够安全,所以浏览器为了用户隐私,设置了严格的同源策略。
限制范围
如果非同源,共有三种行为受到限制。
- 无法读取非同源网页的 Cookie、LocalStorage 和 IndexedDB。
- 无法接触非同源网页的 DOM。
- 无法向非同源地址发送 AJAX 请求(可以发送,但浏览器会拒绝接受响应)。
同源策略限制的是数据访问,我们引用CSS、JS和图片的时候,其实并不知道他的内容,我们只是在引用。
那么,当我们面对不同网站要互相访问数据的需求时,我们该如何跨域?答:CORS 和 JSONP
什么是跨域
跨域就是为了摆脱同源策略,跨网站访问数据
跨域方法1:CORS
CORS全称是“跨域资源共享”(Cross-origin resource sharing)。它允许浏览器向跨域的服务器,发出XMLHttpRequest请求,从而克服了 AJAX 只能同源使用的限制。
如何使用
允许某个网站访问
response.setHeader('Access-Control-Allow-Origin','http://baodu.com') //允许http://baodu.com访问
const request = new XMLHttpRequest()
request.open('get', 'http://qq.com:8888/friends.json') //直接访问其他网站的数据
request.onreadystatechange = () => {
if (request.readyState === 4 && request.status === 200) {
console.log(request.response)
}
}
request.send()
//成功打出log,因为qq.com:8888添加了Access-Control-Allow-Origin
但是ie不支持CORS,有些网站也不支持CORS,那该怎么办?用JSONP
跨域方法2:JSONP
举例:如果网站B希望访问网站A的数据,用JSONP如何实现?
第一步(网站A):网站A将数据放到一个js文件中
window['{{xxx}}']({{ data }})
第二步(网站A):将数据替换到js文件中
···
} else if (path === '/friends.js') {
if (request.headers["referer"].indexOf("http://hack.com:9990") === 0) { //此处进行referer检查,但依然不够安全,所以后面会用到随机数
response.statusCode = 200
response.setHeader('Content-Type', 'text/javascript;charset=utf-8')
const string = fs.readFileSync('./public/friends.js').toString()
const data = fs.readFileSync('./public/friends.json').toString()
const string2 = string.replace('{{ data }}', data).replace('{{xxx}}', query.functionName)
response.write(string2)
response.end()
} else {
response.statusCode = 404
response.end()
}
···
//当有人请求friends.js时,将拿到的包含随机数的query.functionName,以及转变为date字符串的json数据发送回去
第三步(网站B):引用网站A中的js文件
const random = 'hackJSONPCallBackName' + Math.random() //为了不占用任何一个全局变量,加入随机数
//当拿到网站A的数据后,通过window拿到data
//这实际上是一个回调
window[random] = (data) => {
console.log(data)
}
const script = document.createElement('script')
script.src = `http://qq.com:8888/friends.js?functionName=${random}` //
script.onload = () => { //引用后直接删除,以免body中内容越来越多
script.remove()
}
document.body.appendChild(script) //添加到body中
到此,网站B就通过window拿到了网站A的数据
封装
function jsonp(url) {
//因为是异步(需要目标网站调用),所以此处用promise
return new Promise((resolve, reject) => {
const random = 'hackJSONPCallBackName' + Math.random()
window[random] = (data) => {
resolve(data)
}
const script = document.createElement('script')
script.src = `${url}?functionName=${random}` //我用的时候传入一个要获取数据的url
script.onload = () => {
script.remove()
}
script.onerror = () => {
reject()
}
document.body.appendChild(script)
})
}
jsonp('http://qq.com:8888/friends.js')
.then((data) => { //成功后给我一个函数,得到data
console.log(data)
})
什么是JSONP
JSONP是在跨域时,因为当前浏览器,或者其他原因,无法使用CORS,我们需要用到的一种跨域方法。
于是我们在自己网站创建一个script标签,去请求另一个网站的js文件,js文件中会包含一些数据,另一个网站的js文件在自己网站调用一个全局函数(此处是回调,注意是其它网站调用,我只是定义了函数内容),函数中包含我们想要的数据。回调的名字可以是一个随机数,我们将这个随机数用callback的形式传给后台,后台会将这个函数再次返回给我们并执行。
JSONP优点
- 兼容ie
- 可以跨域
JSON缺点
- 因为是script标签,所以无法得知状态码,也不知道响应头,只知道成功或者失败
- 因为是script标签,所以只能发get请求,不支持post