因为浏览器的同源策略导致了跨域。
同源策略限制了从同一个源加载的文档或脚本如何与来自另一个源的资源进行交互。这是一个用于隔离潜在恶意文件的重要安全机制。
URL | 结果 | 原因 |
---|---|---|
http://store.company.com/dir2/other.html | 成功 | 只有路径不同 |
http://store.company.com/dir/inner/another.html | 成功 | 只有路径不同 |
https://store.company.com/secure.html | 失败 | 不同协议 ( https和http ) |
http://store.company.com:81/dir/etc.html | 失败 | 不同端口 ( http:// 80是默认的) |
http://news.company.com/dir/other.html | 失败 | 不同域名 ( news和store ) |
CORS 需要浏览器和后端同时支持。IE 8 和 9 需要通过 XDomainRequest 来实现。
这里有详细的介绍跨域资源共享 CORS 详解
简单点说:
浏览器将CORS请求分成两类:简单请求(simple request)和 非简单请求(not-so-simple request)。
只要同时满足以下两大条件
- 请求方法是以下三种方法之一:
- HEAD
- GET
- POST
- HTTP的头信息不超出以下几种字段:
- Accept
- Accept-Language
- Content-Language
- Last-Event-ID
- Content-Type:只限于三个值application/x-www-form-urlencoded、multipart/form-data、text/plain 凡是不同时满足上面两个条件,就属于非简单请求。
浏览器对这两种请求的处理,是不一样的。
解决方法
/**允许所有的地址访问, 取消跨域拦截 */
res.header("Access-Control-Allow-Origin", "*");
非简单请求是那种对服务器有特殊要求的请求,比如请求方法是PUT或DELETE,或者Content-Type字段的类型是application/json,或者增加了一个头信息字段token
非简单请求的CORS请求,会在正式通信之前,增加一次HTTP查询请求,称为"预检"请求(preflight)。
解决方法
/**允许所有的地址访问, 取消跨域拦截 */
res.header("Access-Control-Allow-Origin", "*");
// #region 复杂请求必须设置 Access-Control-Allow-Methods 和 Access-Control-Allow-Headers
// 其中Access-Control-Allow-Headers中除了基础的Origin, X-Requested-With, Content-Type, Accept
//如果用户有自己新增的头信息则必须加入Access-Control-Allow-Headers
res.header("Access-Control-Allow-Methods", "PUT,POST,GET,DELETE,OPTIONS");
res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept, ' + "token");
// #endregion
CORS请求默认不发送Cookie和HTTP认证信息。如果要把Cookie发到服务器,一方面要服务器同意,指定Access-Control-Allow-Credentials字段。
解决方案:
前端
xhr.withCredentials = true; // 设置允许传递cookies
服务器端
res.header('Access-Control-Allow-Credentials','true');
实现原理:利用 script 标签没有跨域限制的漏洞,网页可以得到从其他来源动态产生的 JSON 数据。JSONP 请求一定需要对方的服务器做支持才可以。
JSONP优缺点:JSONP 优点是简单兼容性好,可用于解决主流浏览器的跨域数据访问的问题。缺点是仅支持 get 方法具有局限性,不安全可能会遭受 XSS 攻击。
JSONP的实现流程:
- 定义一个全局方法window.<前台方法> = ()=>{} 例如:window.cb = ()=>{}
- 生成一个script标签src为<请求地址>?<后台方法>=<前台方法>[例如:http://localhost:9977/a?callback=cb]
- 服务端接受请求 将数据填充至指定格式为"<前台方法>('<数据>')" 前端接收的相应数据格式应该大致
cb("欢迎来到9977端口,我已经放行所有的地址请求")
- 前端通过window.<前台方法>获取数据
换个方法说就好像 后端通过调用前端的方法将数据传递进去。
实现原理:同源策略是浏览器需要遵循的标准,而如果是服务器向服务器请求就无需遵循同源策略。
代理服务器,需要做以下几个步骤:
- 接受客户端请求。
- 将请求转发给目标服务器。
- 获取目标服务其响应的数据
- 将响应转发给客户端
要点
通过代理跨域的要点是代理服务器必须能遵循同源策略接受客户端的请求。
实现原理:
WebSocket协议本身不要求同源策略(Same-origin Policy),也就是某个地址为http://a.com的网页可以通过WebSocket连接到ws://b.com。
但是,浏览器会发送Origin的HTTP头给服务器,服务器可以根据Origin拒绝这个WebSocket请求。所以,是否要求同源要看服务器端如何检查
Websocket 是 HTML5 的一个持久化的协议,它实现了浏览器与服务器的全双工通信,同时也是跨域的一种解决方案。
WebSocket 和 HTTP 都是应用层协议,都基于 TCP 协议。
WebSocket 是一种双向通信协议,在建立连接之后,WebSocket 的 server 与 client 都能主动向对方发送或接收数据。
WebSocket 在建立连接时需要借助 HTTP 协议,连接建立好了之后 client 与 server 之间的双向通信就与 HTTP 无关了。
@TODO