同源策略
什么是同源?在浏览器当前的窗口输入window.origin或者location.origin就可以得到当前的源,比如当前的源就是
源 = 协议 + 域名 + 端口号
如果两个url的协议,域名,端口号都是完全相同的,那么这两个url就是同源的。比如baidu.comh和https://www.baidu.…
如果没有同源
设想一下,如果没有同源会发生什么?比如qq空间,源为user.qzone.qq.com。 此时假设当前用户已经登录,AJAX请求/qqFriends.json(当前用户好友的数据)就可以得到数据。这一切看起来都很正常。此时黑客进行了入侵。假设其中一个好友发了一个钓鱼网站给用户,此时我点开网页,这个网页也请求你的/qqFriends.json。即uest.qzone.qq.com/friends.jso… 此时你的qq好友的列表就被黑客通过AJAX拿走了!
为什么会这样?
因为服务器没法区分发送者,qq空间的JS和钓鱼网站的JS发的请求几乎是没有任何区别的(refer不一样),此时如果后台没有检查refer,那数据就被拿走了。所以为了防止数据被顺走这种情况发生,浏览器采取了严格的同源策略————不同源的页面之间,不允许互相访问数据! 我们做个实验来看看这个情况。我们一个server,然后给不同的端口号,比如一个是8888,另一个是8889,此时在8888端口的服务器中请求一个js,js的内容是请求另一个端口的json数据。
else if (path === '/friends.json'){
response.statusCode = 200;
response.setHeader('Content-Type','text/json,charset="utf-8"')
response.write(fs.readFileSync("./public/friends.json"))
response.end()
}
得到报错
此时可以看到,请求已经发送成功了,但是并没有得到数据
小疑问:
1.为什么www.baidu.com访问baidu.com也算是跨域呢?
因为历史上,曾经出现过不同公司共用域名的情况,这个时候可能www.baidu.com和baidu.com可能并不是一个公司,为了安全起见,只要不同一律不同源
2.为什么端口不同也算跨域?
因为历史上服务器很少,很多时候一个端口一个公司,可能你们两个公司url完全一样,只是端口不同而已。
3.为什么跨域可以使用css,js和图片?
因为同源策略限制的是数据访问,我们引用css,js和图片的时候,我们只是在引用,并不知道他的内容
CORS
那么问题来了,怎么解决跨域,目前主流解决跨域方法的总共有两种,先介绍主流的一种:CORS。
其实浏览器作出同源策略的时候就给留了门,只要设置一个响应头:Access-Control-Allow-Origin然后后面跟要跨域的网站就好了,这是MDN的详细文档
此时在我们的代码中加入这句话,发现我们得到了数据!
else if (path === '/friends.json'){
response.statusCode = 200;
response.setHeader('Access-Control-Allow-Origin',"*")
response.setHeader('Content-Type','text/json,charset="utf-8"')
response.write(fs.readFileSync("./public/friends.json"))
response.end()
}
此时问题得到了圆满的解决,但是有些浏览器并不支持CORS。
JSONP
对于IE浏览器的不支持CORS,前端工程师想出了JSONP的方法进行跨域。
- JSONP的思路
因为我们不能用CORS,但是又要获得数据,我们用一个JS把它包起来,然后引用这个JS不就好了?
- 于是一个JSONP的步骤出来了
- 将要得到的数据写在同源的一个js中,取名friends.js
- 要进行跨域的网站用一个script标签引用这个friends.js
- 在要进行跨域的网站事先定义好一个函数,然后在friends.js中执行这个函数,并且执行时的参数就是要得到的数据
- 此时我们就通过这个回调函数得到了要得到的数
在此基础上,我们进行改进,比如这个回调函数的名字不写死,比如我们在服务器中直接将friends.js写好,不用专门写一个。最后我们得到了想要的数据。这是JSONP的代码
要得到数据的网站的js:
const script = document.createElement("script");
const num = parseInt(Math.random()*10)+1;
script.src = `http://localhost:8889/friends.js?callback=JSONP${num}`
document.head.appendChild(script)
script.onload = ()=>{
script.remove()
}
window[`JSONP${num}`] = (data)=>{
console.log(data);
}
friends.js
else if (path === '/friends.js'){
response.statusCode = 200;
response.setHeader('Content-Type','charset="utf-8"')
const data = fs.readFileSync('./public/friends.json')
let string = `window.xxx({{data}})`.replace('{{data}}',data).replace('xxx',query.callback)
response.write(string)
response.end()
}
面试中,可能会遇到一下的JSONP的问题,这是回答:
- JSONP是什么?
JSONP是在跨域时由于某些原因,不支持CORS,必须用另一种方式跨域,于是我们请求一个JS文件,这个文件会执行一个回调,回调内就有我们要的参数
- 这个回调的名字是什么?
这个回调的名字是可以随机生成的随机数,我们以callback的参数传给后台并执行并执行
- JSONP的优缺点: 优点:1.兼容IE 2.可以跨域 缺点:1.不能获得AJAX的状态 2.只能发get请求