浏览器出于安全性考虑,有一个最基本的同源策略 限制了网站和不同源的资源进行交互。但是也有相应的解决方案:跨域资源共享(CORS) 。
同源策略
源的定义
如果两个URL的协议、端口和主机都相同的话,这两个URL是同源的。相反,如果有一个不同那么就是不同源的。
下面的表给出了和URLhttp://demo.com/dir/page.html
进行比对的示例
URL | 结果 | 原因 |
---|---|---|
demo.com/dir/test.ht... | 同源 | 只有路径不同 |
demo.com/dir/inner/p... | 同源 | 只有路径不同 |
demo.com/dir/page.ht... | 不同源 | 协议不同 |
demo.com:8080/dir/page.ht... | 不同源 | 端口不同 |
test.demo.com/dir/page.ht... | 不同源 | 主机不同 |
源的继承
通过页面脚本(比如Window.open()
等API)打开的about:blank
或javascript
URL会继承父脚本的源
而data:
URL会有一个新的、空的上下文
文件协议
通过文件协议file://
加载的文件视为不透明来源,在发送网络请求时,Origin
字段会是null
值。
而在这些文件加载同目录文件时,有些浏览器可能会将这些同目录文件视为同源文件,这些就取决于浏览器实现了
更改源(已弃用)
通过赋值document.domain
可以把当前页面的源修改为当前域或者当前域的父域
比如一个URL是http://test.demo.com
的页面,可以在页面执行以下js来修改源
ini
document.domain = 'http://demo.com'
网络访问
一般来说,嵌入跨域资源是允许的,比如iframe、css、图片、视频、音频,但是不允许读取这些资源。
对于XMLHttpRequest
或是Fetch
,同源策略并不是限制了请求发出,而是限制了页面获取请求的响应。
CORS
CORS的基本思想是:客户端在发送请求时携带一个origin表明请求源,然后服务器在跨域请求中提供一个响应头,指示哪些源可以访问资源。
基本工作原理
第 1 步:客户端(浏览器)请求
当浏览器发出跨源请求时,浏览器会添加一个 Origin 标头,其中包含当前源站(架构、主机和端口)。
第 2 步:服务端响应
在服务器端,当服务器看到此标头并希望允许访问时,需要将 Access-Control-Allow-Origin 标头添加到用于指定请求来源的响应中( * 表示允许任何来源,但是在涉及身份凭证时不允许设置)。
第 3 步:浏览器收到响应
当浏览器看到此响应带有相应的 Access-Control-Allow-Origin 标头时,就会允许与客户端网站共享响应数据。
预检请求
简单请求
简单请求不会触发预检请求,但是还是会有同源策略的限制。
简单请求的条件如下
- 使用的请求方法为 GET、HEAD、POST;
- 使用的请求头只能是一些浏览器默认设置的头部,比如:
-
- Accept
- Accept-Language
- Content-Language
- Content-Type,但只限于以下三种:
-
-
- application/x-www-form-urlencoded
- multipart/form-data
- text/plain
-
复杂请求
而当浏览器需要发送一个复杂请求到不同源的服务端时,就会在实际请求消息发送之前先发送一个options请求方法的预检请求,如下
bash
OPTIONS /data HTTP/1.1
Origin: https://example.com
Access-Control-Request-Method: DELETE
而服务器则需要响应预检请求并且提供CORS相关的标头信息
服务器还可以响应Access-Control-Max-Age
标头用于指定缓存预检结果的有效期(以秒为单位),这样客户端就不需要每次发送复杂请求都发出预检请求
附带身份凭证的请求
跨域请求默认是不会携带身份凭证信息的。需要设置XMLHttpRequest
对象的withCredentials
属性为true,或者是fetch
请求的credentials
选项为'include'
。
在响应附带身份凭证或者需要设置Set-Cookie
响应标头的跨域请求时:
Access-Control-Allow-Origin
、Access-Control-Allow-Headers
、Access-Control-Allow-Methods
这三个CORS响应头都不能设置为*
通配符- 响应中携带
Access-Control-Allow-Credentials:true
CORS响应标头字段
Access-Control-Allow-Origin
指定允许访问源
Access-Control-Allow-Methods
指定允许访问的请求方法
Access-Control-Allow-Headers
用于预检请求的响应,指定实际请求允许携带的标头
Access-Control-Expose-Headers
在跨源访问时,浏览器脚本默认只能拿到基本的响应头:Cache-Control、Content-Language、Content-Type、Expires、Last-Modified、Pragma,如果要访问其他头,则需要服务器在该响应头进行设置
Access-Control-Max-Age
指定预检请求的结果能被缓存多久
Access-Control-Allow-Credentials
指定当浏览器的credentials
设置为true时是否允许浏览器读取response的内容。
如果是对预检请求响应的,则指定实际请求是否可以使用credentials
。
CORS请求标头字段
这些请求标头字段无需手动设置,浏览器会自动设置
Origin
表明请求源
Access-Control-Request-Method
用于预检请求表明实际请求所用的方法
Access-Control-Request-Headers
用于预检请求表明实际请求所携带的字段