为什么会有跨域?因为浏览器的同源策略。
同源策略
源相同。
如何区别是否是同一个源?
- 协议相同
js 中通过 location.protocol 获取协议。
http、https是不同协议。 - 端口相同
js 中通过 location.port 获取端口。
HTTP 端口默认是 80 。
HTTPS 端口默认是 443 。 - 域名相同,包括主域、子域名。
js 中通过 location.hostname 获取域名。
http://fanyi.youdao.com。fanyi 是子域,youdao.com 是主域。www.youdao.com 与 youdao.com 也是属于不同的域名。
同源策略限制了从同一个源加载的文档(document,主要是 dom 相关)或脚本(js,主要指 XMLHttpRequest )如何与来自另一个源的资源进行交互。
Cookie中的同源只关注域名,忽略协议和端口。
个人理解主要就是两方面:
- 前端与前端的同源限制
页面中引入 iframe ,iframe 的源是否与当前页的源需要相同。 - 前端与后端的同源限制
前端的页面的源需要与后端提供的接口的源相同。
如果没有同源策略,会有什么危害?
- 获取文档中的 dom 信息
可以利用 iframe 嵌套一些正规网站,除了网址不一样,其他都一样(效果有点类似钓鱼网站,不过钓鱼不是用 iframe ,是做的样式一样)。可以获取用户信息(密码、支付信息等)。 - CSRF(跨站请求伪造)
获取其他正规网站的 cookie ,然后用 cookie 模拟用户操作,如支付、转账。
所以同源策略是一个用于隔离潜在恶意文件的重要安全机制。
解决跨域通信
- 前端与前端的跨域
不同主域的前端页面之间,不能实现互相通信。所以主要解决的是主域相同的,子域不同的互相通信问题。- document.domain
当前页和 iframe 页面都设置相同的 document.domain,则当前页可以拿到 iframe 中的 dom 节点。
document.domain 只能设置成自身或更高一级的父域,且主域必须相同。
这种方式非常适用于 iframe 跨域的情况。 - postMessage
window.postMessage(message,targetOrigin)
方法是 HTML5 新引进的特性,可以使用它来向其它的 window 对象发送消息,无论这个 window 对象是属于同源或不同源。
调用postMessage方法的window对象是指要接收消息的那一个window对象,该方法的第一个参数message为要发送的消息,类型只能为字符串;第二个参数targetOrigin用来限定接收消息的那个window对象所在的域,如果不想限定域,可以使用通配符*
。
需要接收消息的window对象,可是通过监听自身的message事件来获取传过来的消息,消息内容储存在该事件对象的data属性中。
可以实现双向通信,但是当前页获取不到 iframe 中的 dom 节点。
当前页:
iframe:
- document.domain
- 前端与后端的跨域
- 代理
同源的后端去请求获取不同源的数据,再返给同源的前端。 - JSONP
主要原理是利用了script 标签可以跨域请求的特性,由其 src 属性发送请求到服务器,服务器返回 JavaScript 代码,浏览器接受响应,然后就直接执行了,这和通过 script 标签引用外部文件的原理是一样的。
只有 get 方法( script 加载资源就是 get ),前端本地定义方法,返回值为执行该方法,本地定义的方法名称需要和返回值执行的方法名一样。
后端代码: 前端代码: - CORS
跨源资源共享机制
- 代理
CORS
跨源资源共享机制(Cross-Origin Resource Sharing)允许 Web 应用服务器进行跨域访问控制,从而使跨域数据传输得以安全进行。浏览器支持在 API 容器中(如 XMLHttpRequest 或 Fetch )使用 CORS,以降低跨域 HTTP 请求所带来的风险。
CORS 需要客户端和服务器同时支持。
跨域资源共享标准
跨域资源共享标准新增了一组 HTTP 首部字段,允许服务器声明哪些源站有权限访问哪些资源。另外,规范要求,对那些可能对服务器数据产生副作用的 HTTP 请求方法(特别是 GET 以外的 HTTP 请求,或者搭配某些 MIME 类型的 POST 请求),浏览器必须首先使用 OPTIONS 方法发起一个预检请求(preflight request),从而获知服务端是否允许该跨域请求。服务器确认允许之后,才发起实际的 HTTP 请求。在预检请求的返回中,服务器端也可以通知客户端,是否需要携带身份凭证(包括 Cookies 和 HTTP 认证相关数据)。
跨域资源共享标准允许在下列场景中使用跨域 HTTP 请求:
XMLHttpRequest 或 Fetch 发起的跨域 HTTP 请求。
Web 字体 (CSS 中通过
@font-face
使用跨域字体资源)WebGL 贴图
使用 drawImage 将 Images/video 画面绘制到 canvas
样式表
scripts
访问控制场景
使用三个场景来解释跨域资源共享机制的工作原理。这些例子都使用 XMLHttpRequest 对象。
简单请求
某些请求不会触发 CORS 预检请求。本文称这样的请求为“简单请求”,请注意,该术语并不属于 Fetch (其中定义了 CORS)规范。
若请求满足所有下述条件,则该请求可视为“简单请求”:
使用下列方法之一
GET
HEAD
POST
Fetch 规范定义了对 CORS 安全的首部字段集合,不得人为设置该集合之外的其他首部字段。该集合为:
Accept
Accept-Language
Content-Language
Content-Type (需要注意额外的限制)
DPR
Downlink
Save-Data
Viewport-Width
Width
Content-Type 的值仅限于下列三者之一
text/plain
multipart/form-data
application/x-www-form-urlencoded
请求中的任意 XMLHttpRequestUpload 对象均没有注册任何事件监听器;XMLHttpRequestUpload 对象可以使用 XMLHttpRequest.upload 属性访问。
请求中没有使用 ReadableStream 对象。
预检请求
“预检请求”要求必须首先使用 OPTIONS 方法发起一个预检请求到服务器,以获知服务器是否允许该实际请求。"预检请求“的使用,可以避免跨域请求对服务器的用户数据产生未预期的影响。
当请求满足下述任一条件时,会首先发送预检请求:
使用了下面任一 HTTP 方法
PUT
DELETE
CONNECT
OPTIONS
TRACE
PATCH
人为设置了对 CORS 安全的首部字段集合之外的其他首部字段。该集合为:
Accept
Accept-Language
Content-Language
Content-Type (需要注意额外的限制)
DPR
Downlink
Save-Data
Viewport-Width
Width
Content-Type 的值不属于下列之一
text/plain
multipart/form-data
application/x-www-form-urlencoded
请求中的 XMLHttpRequestUpload 对象注册了任意多个事件监听器。
请求中使用了 ReadableStream 对象。
附带身份凭证的请求
一般而言,对于跨域 XMLHttpRequest 或 Fetch 请求,浏览器不会发送身份凭证信息。如果要发送凭证信息,需要设置 XMLHttpRequest 的 withCredentials 为 true 。
如果服务器端的响应中未携带 Access-Control-Allow-Credentials: true ,浏览器将不会把响应内容返回给请求的发送者。
对于附带身份凭证的请求,服务器不得设置 Access-Control-Allow-Origin 的值为“*”。