计算机网络:同源策略
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更好
 
- 代理 - 利用代理将不同源的资源代理到同源的资源 