计算机网络:同源策略
1、介绍
- 同源策略:禁止一个源(origin)的脚本和文档和另一个源的脚本和文档交互。
- 什么是同源:两个URL的协议(protocol)、端口(port)和域名(domain)都相同,这就是同源。
- 同源策略的目的:防止恶意的网站窃取用户的cookie,或者其他敏感信息。
- 同源策略的实现:浏览器会在同源的情况下,允许脚本和文档进行交互。
- 同源策略的限制:不能读取对方的
cookie
、localStorage
、sessionStorage
、indexedDB
、webSocket
、EventSource
、XMLHttpRequest
、location
、history
等等。
2、问题回答
- 问1:如果两个源产生过多交互会有什么影响?
影响很大的就是恶意的网站窃取用户的cookie,或者其他敏感信息。这样数据就不安全了,所以同源策略是非常重要的。
- 问2:应不应该允许网站提交cookie到不同源的服务器?
这个是不允许的,但是你自己配置了跨域是可以的。
- 问3:为什么不禁用不同源的js?
因为有些js是第三方提供的,我们引用cdn的js,引用组件。
- 问4:应不应该允许不同源的js修改dom?
这个是允许的,比如百度统计,百度地图等,如果不允许不同源的js修改dom,那么就无法统计用户的地理位置,无法使用百度地图等功能。
- 问5:应不应该允许不同源的js获取远程图片内容?
这个是不允许的,如果能远程执行代码,那么就有可能获取到敏感信息和人家私密数据,这个肯定不行的。
- 问6:应不应该允许网站提交数据到不同源的服务器?
这个是不允许的,如果能提交数据,那么就会被别人收集你的数据,然后提交到自己的服务器上,这等于抢劫,不劳而获,这个也肯定不行的。
3、跨域请求技术
Jsonp技术
Jsonp的原理:就是利用script标签的src属性没有跨域限制来实现的。
Jsonp的优缺点:
- 缺点:只能get请求
- 优点:浏览器的兼容性好
Jsonp的实现及应用:
模拟服务端:
server.js
,启动方式:node server.js
(需要安装node.js)
//模拟服务端
let http = require("http")
let url = require("url")
//模拟数据
let data;
http.createServer((req, res) => {
let urlobj = url.parse(req.url, true)
console.log("打印参数:id={} & cbk={}",[urlobj.query.id,urlobj.query.cbk]);
switch (urlobj.pathname) {
case "/api/jsonp":
//返回结果格式:JSONP.callbacks[callbackId](json内容)
//示例:JSONP.callbacks[1]({"name":"小猿编程秘籍","age":18})
let returnData =data[urlobj.query.id]
res.end(`${urlobj.query.cbk} (${JSON.stringify(returnData)})`)
break;
default:
res.end("404")
}
}).listen(8888, () => {
console.log("start");
//模拟数据初始化
data = getData();
})
function getData() {
return {
"1":{
id: 1,
name: '小猿编程秘籍',
age: 18
},
"2": {
id: 2,
name: '小白',
age: 18
},
"3": {
id: 3,
name: '小红',
age: 18
},
}
}
- 模拟客户端:
jsonp.html
,启动方式:直接点击这个文件用浏览器打开就可以
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="utf-8">
<title>JSONP测试</title>
</head>
<body>
<div style="text-align: center">
<input type="text" id="idVal" style="margin-top: 30px;" />
<button id="btn" onclick="selectData()">查询</button>
<br/>
<h4>JSONP客户端测试结果:</h4>
<br>
<textarea id="callback" cols="50" rows="10" name="returnData"></textarea>
</div>
<script >
//创建JSONP函数
function JSONP({
url,
params = {},
callbackKey = 'cbk',
callback
})
{
//1、定义唯一id
JSONP.callbackId = JSONP.callbackId || 1;
let callbackId = JSONP.callbackId;
//2、避免污染
JSONP.callbacks = JSONP.callbacks || [];
JSONP.callbacks[callbackId] = callback;
//3、添加参数
params[callbackKey] = `JSONP.callbacks[${callbackId}]`;
//4、等到最终参数字符串
const paramString = Object.keys(params).map(key => {
return `${key}=${encodeURIComponent(params[key])}`
}).join('&')
//5、创建 script 标签 添加到页面中
const script = document.createElement('script');
script.setAttribute('src', `${url}?${paramString}`);
document.body.appendChild(script);
// 6、保证唯一
JSONP.callbackId++;
}
function selectData(){
let id = document.getElementById('idVal').value;
//使用JSONP函数
JSONP({
url: 'http://localhost:8888/api/jsonp',
params: {
id: id
},
callbackKey: 'cbk',
callback (res) {
document.getElementById('callback').innerHTML = JSON.stringify(res)
}
})
}
</script>
</body>
</html>
测试:先启动服务端,然后打开客户端
- 服务端启动
- 客户端打开,请求结果
跨域资源共享(CORS)
- 概念:跨域资源共用(Cross-Origin Resource Sharing)使用额外HTTP头允许指定的源和另一个源进行交互
- 预检(preflight):客户端和服务端在正式通信前,浏览器会增加一次HTTP查询请求,这就是预检请求
- CORS请求跨域实现流程:
- CORS与JSONP的对比,个人认为JSONP更好
代理
利用代理将不同源的资源代理到同源的资源