同源策略与跨域

文章详细介绍了Web的同源策略,以及由于此策略带来的跨域问题。文中提到了多种跨域解决方案,如JSONP、CORS、WebSocket、服务端代理、Nginx反向代理、Webpack代理配置、window.postMessage、document.domain和iframe通信等,分析了各种方法的原理、优缺点和适用场景。
摘要由CSDN通过智能技术生成

一,

概念:源(origin)是由协议、IP地址/域名、端口号组成。

二,同源

 概念:两个URL的协议、IP地址/主域名、端口号均一致,即为同源。(子域名可以不同:例如http:www.baidu.com/search和http:www.baidu.com/jump)

同源策略:浏览器为了防止XSS、CSRF等攻击,而设计了同源策略,来保障网络安全。

三,非同源

概念:同源的任意一个条件不满足,即为非同源。

非同源的限制

  • 可以访问其他源的图片、js、css(包括<img src="其他源地址">,<link href="其他源地址">,<script src="其他源地址">)
  • form表单可以提交,但是不需要返回
  • ajax请求发送之后,浏览器会拦截响应(因此跨域情况下服务端是可以收到ajax的请求)
  • 不可以访问其他源的cookielocalStorge
  • 不能操作dom
  • 不能访问iframe嵌入的其他页面内容

四,跨域

所谓跨域CROS,即cross origin resource sharing,跨源资源共享。用来解决浏览器的同源限制,访问其他源的资源。

五,跨域的解决方式

5.1 jsonp方式

原理:他是一个传统的跨域解决方式,主要是利用script标签可以引用其他源的地址。

1,客户端动态创建script标签,通过自定义一个回调函数拼接到src属性的URL中,来实现向接口发送请求

2,服务器接收到请求后,根据回调函数动态生成客户端想要的脚本,并以字符串的形式返回

3,客户端将服务器返回的字符串当成js解析,并执行

实现示例

<!-- 浏览器 -->
<!-- 通过src属性请求app/datalist接口,传参callback=getData -->
<script src="http://kuayu.com/app/datalist?callback=getData">

<script>
// 定义回调函数 和参数
function getData(data) {
    console.log(data); // init success
}
</script>
// 服务器

// 定义接口app/datalist
app.get('/app/datalist', (req, res) => {
    let fn = req.param.callback; // 获取callback参数的值
    const resData = 'init success';
    res.send(fn + resData); // 服务端响应回调函数和参数
})

特点

优点

  • 兼容性较好(可兼容老版本的IE浏览器)

缺点

  • 不能控制请求过程。因为他只是一个script标签,由浏览器自动发起的请求
  • 只支持get请求方式

5.2 CORS方式

原理

就是在请求头添加一个身份来源标识,同时服务器端根据标识判断是否可以访问,如果允许则添加一个标记并返回响应。CORS同样分为简单请求和非简单请求两种。

简单请求:请求方式是get、post或head方式;请求头只能设置基础的安全字段accept、accept-lauage、content-language、content-type这几个字段)

非简单请求:除简单请求之外的其他请求。例如put、delete

实现示例:

简单请求:http请求头只需设置origin属性为跨域的源即可

GET /api HTTP/1.1
Origin: http://www.xxx.com	//本次请求来自哪个源
Host: http://www.xx.com		//请求的第三方API
Accept-Language: en-US

服务器设置Access-Control-Allow-Origin的支持访问的源。服务器收到响应会和origin的源进行对比,如果在支持的源内,则返回响应,同时在响应的header中添加设置的属性

Access-Control-Allow-Origin: http://www.xxx.com		//请求允许的源
Access-Control-Allow-Credentials: true				//是否允许cookie
Content-Type: text/html; charset=utf-8

复杂请求:

客户端会先发一次options预请求服务器检查Origin、Access-Control-Request-Method和Access-Control-Request-Headers字段,确认允许访问之后,再做出响应,然后客户端再发正式的请求。

options预请求和响应都没有请求体body

特点:

  • w3c增加的新特性,需要服务端支持,同时不兼容IE9以下浏览器
  • 相比jsonp,支持的方法更多,更灵活
  • 可以根据请求方式的不同灵活选用简单请求和复杂请求方式

5.3 websocket方式

原理

websocket可以实现浏览器和服务器的双向通信,因此不会有跨域的问题。

特点

  • 因为他是常链接,所以需要根据业务场景来判断是否选用
  • 原生的websocketAPI使用起来较麻烦,需要引入三方库例如socket.io来实现

5.4 服务端代理

原理:因跨域是浏览器和服务器之间的限制,而服务器和服务器之间没有这种限制,因此增加一个代理服务器,来解决跨域问题。

主要流程是:接受客户端的请求=>将请求转发给服务器=>接受服务器的请求=>将响应转发给客户端

特点

  • 客户端和服务器不需要添加额外的判断逻辑和代码
  • 需要额外实现一个服务器端的代码开发(例如node中间件)

5.5 nginx反向代理

原理

与服务器代理原理类似,只是这个需要搭建一个nginx服务器来转发请求。

特点

  • 只需要修改nginx.conf文件中的配置即可,不需修改任何代码,实现简单
  • 支持所有浏览器
  • 不会影响服务器的性能
  • 支持session

实现示例

 server{
        listen 8080;
        server_name  http://www.xx.com;
 
        location / {
            proxy_pass http://www.yy.com:8000; # 反向代理
        }
}

5.6 webpack的proxy代理配置

原理

通过配置webpack的配置文件模拟本地服务的接口,而非代理的方式,相当于直接请求本地的服务devServer。

特点:

  • 只需要修改webpack的配置文件即可
  • 只支持本地跨域解决方案,线上环境无效

实现示例

module.exports = {
    devServer: {
        proxy: {
            '/api': {
                target: 'http://xxx.com',
                pathRewrite: {'^/api' : ''}, 
                changeOrigin: true, // target如果是域名时,需要这个参数
                secure: false, // 设置支持https协议的代理
            }
        }
    }
}

5.7 window.postMessage

原理

window.postMessage是H5新增的特性,主要为了解决跨窗口之间的通信,也支持iframe下进行通信。可以利用这个方法来绕过浏览器的跨域。

特点

  • 不需要服务端处理,客户端通过这个方法绕过了同源策略
  • 是H5新增的window的原生属性
  • 可支持多窗口通信

使用示例

window.postMessage(message, targetOrigin)方法主要入参分别为:

  • message:发送的消息内容
  • targetOrigin:要发送的目标网址(设置*号为向所有窗口发送消息)

window.onmessage函数接受的event参数提供了3个属性值:

  • event.source:发送消息的窗口
  • event.origin: 消息发向的网址
  • event.data: 消息内容
// 标签一,发送
window.postMessage({ name: '张三', age: 18 }, 'http://127.0.0.1:8080');
window.onmessage = function(e) {
    console.log(e.data); // 接收另一个标签的响应
}

// 标签二,接收、响应
window.onmessage = function(e) {
    console.log(e.data);
    e.source.postMessage('收到', e.origin); // 向接收来的标签响应一个消息
}

5.8 document.domain + iframe

原理

两个相同的二级域名(例如a.test.com和b.test.com)之间,可以同时设置document.domain = "test.com"基础域名,强制设置为同域,来实现通信。因此可以利用iframe嵌套网页,然后在他的onload事件中,调用contentWindow属性来获取另一个页面的值。

实现示例

<!-- 页面1 -->
<iframe src="http://b.test.com" onload="load()" id="frame"></iframe>

<script>
    documnet.domain = "test.com"; // 定义二级域名
    load() {
        console.log(frame.connectWindow.text); // 获取另一个页面的text属性
    }
</script>

<!-- 页面2 -->
<script>
    documnet.domain = "test.com"; // 定义二级域名
    var text = "好利来"; // 定义变量
</script>

页面1实现方式2:

<!-- 页面1 -->
<iframe src="http://b.test.com" id="frame"></iframe>

<script>
    documnet.domain = "test.com"; // 定义二级域名
    documnet.getElementById("frame").onload = function() {
        console.log(this.connectWindow.text); // 获取另一个页面的text属性
    }
</script>

特点

  • 只需客户端修改代码
  • 条件限制必须是二级域名相同

5.9 window.name + iframe

原理

window.name属性有一个特性,就是无论是否同源,只要在一个窗口,就可以访问前一个网页设置的window.name属性值;而且他的值存储最大可以支持到2M。因此可以将变量存到window.name中,然后利用iframe嵌套访问。

实现示例

<!-- 页面1 -->
<iframe src="http://b.test.com" onload="load()" id="frame"></iframe>

<script>
    load() {
        // 获取另一个页面的window.name属性
        const params = frame.connectWindow.name;
        const arrs = params.split(";");
    }
</script>

<!-- 页面2 -->
<script>
    window.name = "token=xxx;id=xxx"; // 存储一些信息
</script>

特点

  • window.name容量很大,可以存储2M的字符串
  • 同样是绕过浏览器的同源策略,不需要修改服务端代码
  • 必须要监听窗口的window.name属性变化,影响页面的性能

5.10 location.hash + iframe

原理

三个页面。需要通过实现一个中间页,利用iframe的location.hash来传值。例如a.html和c.html是同域;b.html是跨域。

a.html和b.html、b.html和c.html不同域通过location.hash传值并访问,且是单向通信;

而c.html和a.html是同域,通过window.parent.parent属性去访问

实现示例

特点

  • 实现起来繁琐,只是说明有这种实现方式而已
  • a.html和b.html不同域只能单向传值

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

妍思码匠

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值