在web浏览器中,同源策略 限制了从同一个源加载的文档或脚本如何与来自另外一个源的资源进行交互。这是一个用于隔离潜在恶意文件的重要安全机制。java
若是两个页面的node
三者都相同,则两个页面具备相同的源。ios
举例说明 http://store.example.com/dir/page.html
同源检测的示例:nginx
URL | 结果 | 缘由 |
---|---|---|
http://store.example.com/index.html |
成功 | |
http://store.example.com/dir/another.html |
成功 | |
https://store.example.com/index.html |
失败 | 不一样协议 ( https和http ) |
http://store.example.com:81/index.html |
失败 | 不一样端口 ( 81和80) |
http://news.example.com/index.html |
失败 | 不一样域名 ( news和store ) |
http://example.com/index.html |
失败 | 不一样域名 (store是一个单独的自域) |
如下为4种能够遇到的跨源web
页面可能会因某些限制而改变他的源。ajax
设置 document.domain
的值,为其当前域或其当前域的父域。express
场景http://store.example.com/dir/page.html
文档中的一个脚本执行如下语句document.domain = "company.com"
便可经过同源检测
同源策略控制了不一样源之间的交互。
使用 CORS
容许跨源访问。
场景 由浏览器发起的跨域 HTTP 请求 (这个你们接触的最多)
Javascript的APIs中,容许文档间直接相互引用。可是当两个文档的源不一样时,一些引用方式将对 API对象的访问添加限制
可使用window.postMessage
场景 使用
<iframe>
嵌套的时候,父子页面的通讯
存储在浏览器中的数据,如localStorage和IndexedDB,以源进行分割。每一个源都拥有本身单独的存储空间,一个源中的Javascript脚本不能
对属于其它源的数据进行读写操做
。
场景 null
MDN的网站给出了这样的2种解释:
CORS (Cross-Origin Resource Sharing,跨域资源共享)是一个系统,它由一系列传输的HTTP头组成,这些HTTP头决定浏览器是否阻止前端 JavaScript 代码获取跨域请求的响应。 CORS 给了web服务器这样的权限,即服务器能够选择,容许跨域请求访问到它们的资源。跨域资源共享(CORS) 是一种机制,它使用额外的 HTTP 头来告诉浏览器 让运行在一个 origin (domain) 上的Web应用被准许访问来自不一样源服务器上的指定的资源。当一个资源从与该资源自己所在的服务器不一样的域、协议或端口请求一个资源时,资源会发起一个跨域 HTTP 请求。
实际上着两种解释都同样。我给出这样的理解:CORS赋予服务端(一般所说的后端)一个能力,本身控制哪些浏览器的请求能够访问到它的资源,来解决跨域问题。
附:全部的 CORS
头
HTTP头 | 功能 |
---|---|
Access-Control-Allow-Origin |
指示请求的资源能共享给哪些域。 |
Access-Control-Allow-Credentials |
指示当请求的凭证标记为 true 时,是否响应该请求。 |
Access-Control-Allow-Headers |
用在对预请求的响应中,指示实际的请求中可使用哪些 HTTP 头。 |
Access-Control-Allow-Methods |
指定对预请求的响应中,哪些 HTTP 方法容许访问请求的资源。 |
Access-Control-Expose-Headers |
指示哪些 HTTP 头的名称能在响应中列出。 |
Access-Control-Max-Age |
指示预请求的结果能被缓存多久。 |
Access-Control-Request-Headers |
用于发起一个预请求,告知服务器正式请求会使用那些 HTTP 头。 |
Access-Control-Request-Method |
用于发起一个预请求,告知服务器正式请求会使用哪种 HTTP 请求方法。 |
Origin |
指示获取资源的请求是从什么域发起的。 |
功能概述 TL;DR
规范要求,对那些可能对服务器数据产生反作用的 HTTP 请求方法(特别是 GET 之外的 HTTP 请求,或者搭配某些 MIME 类型的 POST 请求),浏览器必须首先使用 OPTIONS 方法发起一个预检请求(preflight request),从而获知服务端是否容许该跨域请求。服务器确认容许以后,才发起实际的 HTTP 请求。在预检请求的返回中,服务器端也能够通知客户端,是否须要携带身份凭证(包括 Cookies 和 HTTP 认证相关数据)。
一句话概述:非简单请求时,会先发送预检请求,容许后再发送实际请求
附:node-express框架下,服务端的跨域设置
app.all('*', function (req, res, next) { res.header('Access-Control-Allow-Origin', '*'); res.header('Access-Control-Allow-Headers', 'Content-Type'); res.header('Access-Control-Allow-Methods', '*'); next(); });
CORS请求默认不发送Cookie和HTTP认证信息,若是想要知道用cookie就要注意3点
- Client端
new XMLHttpRequest()
中withCredentials
设置为true
- Serive端
HTTP头
Access-Control-Allow-Credentials
设置为true
- Serive端
HTTP头
Access-Control-Allow-Origin
不能设为星号,必须指定明确的、与请求网页一致的域名
因此上面的例子要想发送cookie
// service端 `res.header('Access-Control-Allow-Credentials', true);` `res.header('Access-Control-Allow-Origin', '具体的域名');` // client端 Jquery `ajax()` `xhrFields: {withCredentials: true}` Axios `axios.defaults.withCredentials = true`
上面讲了简单请求,但那些才是简单请求呢?咱们称:若不会触发 CORS 的预检请求,称这样的请求为“简单请求”
如下为简单请求:
- HTTP Method 组成只能是如下几种
- GET
- POST
- HEAD
- HTTP Headers 组成
- Accept
- Accept-Language
- Content-Language
- Last-Event-ID
Content-Type 只包含如下类型 (form表单请求)
- application/x-www-form-urlencoded
- multipart/form-data
- text/plain
注:只有同时知足以上两个条件时,才是简单请求,不然为非简单请求
前面说了这么多预检请求,咱们来说一讲什么是预检请求:
若是咱们在client端发送请求时,例如:
// 原生 var invocation = new XMLHttpRequest(); invocation.setRequestHeader('X-EXAMPLE', 'xixihaha'); invocation.setRequestHeader('Content-Type', 'application/xml'); // axios axios.defaults.headers['X-EXAMPLE'] = 'xixihaha'; axios.defaults.headers['Content-Type'] = 'application/xml';
POST 请求发送一个 XML 文档,该请求包含了一个自定义的请求首部字段(X-EXAMPLE: xixihaha)。另外,该请求的 Content-Type 为 application/xml。所以,该请求须要首先发起“预检请求”。
server端 的HTTP头设置
Access-Control-Allow-Origin: '具体的域名' Access-Control-Allow-Methods: POST, GET, OPTIONS // 可包含的参数 Access-Control-Allow-Headers: X-PINGOTHER, Content-Type // 容许的首部字段 Access-Control-Max-Age: 86400
非简单请求和简单请求无异,若是浏览器的预检请求被服务器接受,则发送实际请求,未被接受则拒绝请求。
跨域不止于此
JSONP
动态建立script标签,而后利用script的src 不受同源策略约束来跨域获取数据
function addScriptTag() { var script = document.createElement("script"); script.src = "http://foo.example?callback=handleResponse"; document.body.appendChild(script); } function handleResponse() { console.log('跨域数据'); };
如下跨域方案不作过多解释
上文提到的postMessage()
nginx
转发,即架设服务器代理
window.name