解决浏览器跨域的N种手段

1,735 阅读5分钟

这是我参与 8 月更文挑战的第 26 天,活动详情查看: 8月更文挑战

前言

`Access to XMLHttpRequest at 'https://xxx.xxx.com' from origin 'https://xxx.xxx.com' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.`。

相信大家都见过这个报错,没错,就这是跨域了。跨域,是指浏览器不能执行其他网站的脚本。它是由浏览器的同源策略造成的,是浏览器对JavaScript实施的安全限制。 这里说明一下,无法跨域是浏览器对于用户安全的考虑,如果自己写个没有同源策略的浏览器,完全不用考虑跨域问题了。是浏览器的锅,对。 同源策略限制了一下行为: Cookie、LocalStorage 和 IndexDB 无法读取 DOM 和 JS 对象无法获取 Ajax请求发送不出去

首先,只有浏览器才会产生跨域,这是因为浏览器的同源策略在限制。同源策略就是 [域名(又称为主机 host),端口(port),协议(protocol)] 要统一,否则就会被浏览器判定为跨域,然后拒绝请求。但是严格的说,浏览器并不是拒绝所有的跨域请求,实际上拒绝的是跨域的读操作。

同源策略并不是不好,它在一定程度上保证了浏览器的安全,只是无法适应现代的潮流,在工程服务化后,不同职责的服务分散在不同的工程中,往往这些工程的域名是不同的,但一个需求可能需要对应到多个服务,这时便需要调用不同服务的接口。

解决跨域请求的N种手段

1、jsonp

利用了 script 不受同源策略的限制

缺点:只能 get 方式,易受到 XSS攻击

2、CORS(Cross-Origin Resource Sharing),跨域资源共享

当使用XMLHttpRequest发送请求时,如果浏览器发现违反了同源策略就会自动加上一个请求头 origin;

后端在接受到请求后确定响应后会在 Response Headers 中加入一个属性 Access-Control-Allow-Origin;

浏览器判断响应中的 Access-Control-Allow-Origin 值是否和当前的地址相同,匹配成功后才继续响应处理,否则报错

缺点:忽略 cookie,浏览器版本有一定要求

支持CORS请求的浏览器一旦发现ajax请求跨域,会对请求做一些特殊处理,对于已经实现CORS接口的服务端,接受请求,并做出回应。

有一种情况比较特殊,如果我们发送的跨域请求为“非简单请求”,浏览器会在发出此请求之前首先发送一个请求类型为OPTIONS的“预检请求”,验证请求源是否为服务端允许源,这些对于开发这来说是感觉不到的,由浏览器代理。

总而言之,客户端不需要对跨域请求做任何特殊处理。

  • Access-Control-Allow-Origin

这是 HTTP 响应首部中的一个字段,

具体格式是: Access-Control-Allow-Origin: <origin> | *

其中,origin 参数的值指定了允许访问该资源的外域 URI。对于不需要携带身份凭证的请求,服务器可以指定该字段的值为通配符,表示允许来自所有域的请求。

如果服务端指定了具体的域名而非*,那么响应首部中的 Vary 字段的值必须包含 Origin。这将告诉客户端:服务器对不同的源站返回不同的内容。例如:

// 只响应来自 http://mozilla.com 的请求
Access-Control-Allow-Origin: http://mozilla.com
  • Access-Control-Allow-Methods

该字段必需,它的值是逗号分隔的一个字符串,表明服务器支持的所有跨域请求的方法。注意,返回的是所有支持的方法,而不单是浏览器请求的那个方法。这是为了避免多次"预检"请求。

具体格式是:Access-Control-Allow-Methods: <method>[, <method>]*

  • Access-Control-Allow-Headers

可支持的请求首部名字。请求头会列出所有支持的首部列表,用逗号隔开。

如果浏览器请求包括Access-Control-Request-Headers字段,则Access-Control-Allow-Headers字段是必需的。它也是一个逗号分隔的字符串,表明服务器支持的所有头信息字段,不限于浏览器在"预检"中请求的字段。

具体格式是:Access-Control-Allow-Headers: <header-name>[, <header-name>]*

  • Access-Control-Allow-Credentials

该字段可选。它的值是一个布尔值,表示是否允许发送 Cookie。默认情况下,Cookie 不包括在 CORS 请求之中。设为 true,即表示服务器明确许可,Cookie 可以包含在请求中,一起发给服务器。

这种方式分为两种请求:

一种是简单请求,另一种是非简单请求

简单请求跨域:

请求方式为HEAD、POST 或者 GET

非简单请求跨域:

非简单请求是那种对服务器有特殊要求的请求,比如请求方法是PUT或DELETE,或者Content-Type字段的类型是application/json

3、代理跨域请求(Nginx)

前端向发送请求,经过代理,请求需要的服务器资源

缺点:需要额外的代理服务器

4、Html5 postMessage 方法

允许来自不同源的脚本采用异步方式进行有限的通信,可以实现跨文本、多窗口、跨域消息传递

缺点:浏览器版本要求,部分浏览器要配置放开跨域限制

5、修改 document.domain 跨子域

相同主域名下的不同子域名资源,设置 document.domain 为 相同的一级域名

缺点:同一一级域名;相同协议;相同端口

6、基于 Html5 websocket 协议

websocket 是 Html5 一种新的协议,基于该协议可以做到浏览器与服务器全双工通信,允许跨域请求

缺点:浏览器一定版本要求,服务器需要支持 websocket 协议

7、document.xxx + iframe

通过 iframe 是浏览器非同源标签,加载内容中转,传到当前页面的属性中

缺点:页面的属性值有大小限制

小结

在实际的工作中,我们使用CROS的方式和后端打配合会比较常见。当然如果把web项目和后端接口项目放到一个域中,这样就不存在跨域问题了。