跨域
跨域分为两种,一种是浏览器端和服务器端之间的跨域,另一种是浏览器中不同标签页之间的跨域。
他们 对于js代码 的针对解决方案,分别是 CORS、同源策略。非 js 比如 form、script标签 都不受到他们影响。
CORS策略
为了安全性,防止恶意网站获取非本站的内容,导致危险行为,浏览器禁止跨域请求,并制定了该行为的一条规则:
跨源请求,即那些发送到其他域(即使是子域)、协议或端口的请求,需要远程服务器提供特殊 header。
这个策略被称为 “CORS”:跨源资源共享(Cross-Origin Resource Sharing)。
这个简单有力的规则,就是互联网安全的基础。
白话翻译过来就是,浏览器中属于本域名的脚本,无法访问任何其他域,除非目标允许访问。
在具体实现上,如果一个请求是跨源的,浏览器在请求中始终会添加 Origin 标头。Origin 的值为确切的源(protocol//host)。对于所有的请求,相应的响应必须始终包含 Access-Control-Allow-Origin:它的值必须包含请求时的 Origin。
两个“源”,需要协议、域名(必须完全相同,子域名也不行)、端口完全相同,才是同源的。
跨源请求
一个跨域的 js 请求,有两种类型:安全的请求,和不安全的请求。
安全请求
如果一个请求满足下面 2 个条件,则该请求是安全的,被允许跨域,不需要其他额外要求:
请求的方式是规范指定的 3 个方法:GET,POST 或 HEAD;
在 1 的基础上,仅下列 4 个请求/响应 header 被允许使用:
Accept,用于表示期望的响应类型;
Accept-Language,用于表示期望的响应的语言设定;
Content-Language,用于响应标头,表示响应的实际语言,
Content-Type,用于响应标头,表示响应的内容类型,并且值只能取 3 种:
- text/plain、application/x-www-form-urlencoded 或 multipart/form-data (不用记)
请求是这样的,就被浏览器发送。随后,服务器检查 Origin,如果同意接受这样的请求,就会在响应中添加标头 Access-Control-Allow-Origin。
不安全的请求
如果请求不满足上面的条件,浏览器视为该脚本在尝试发送不安全的跨域请求,这将进一步约束请求和响应:
首先,浏览器会先发送所谓的“预检(preflight)”请求。预见请求-响应过程使用的标头的特征是含有"Access-Control-"(取得权限) 前缀的标头。
预检请求使用 OPTIONS 方法,它没有 body,有三个 header:
- Origin;
- Access-Control-Request-Method: 带有将发送的请求的方法。
- Access-Control-Request-Headers: 带有将发送的标头列表,内容以逗号分隔。
请求后,如果服务器同意处理请求,那么它会进行响应,此响应的状态码应该为 200,没有 body,具有三个 header:
- Access-Control-Allow-Origin;
- Access-Control-Allow-Methods 必须具有允许的方法;
- Access-Control-Allow-Headers 必须具有一个允许的 header 列表。
如果响应不满足这些条件,响应将被拦截。另外,响应中常常包含可选的"Access-Control-Max-Age",表示此次跨域权限持有的秒数。
响应
不安全的请求,在上述过程发生后,预检成功,浏览器现在发出主请求并得到响应;或者本来就是“规定安全的”请求得到了响应。浏览器将响应传递给网站脚本。网页脚本得到了响应 (Response) 对象。
该响应对象目前只能访问下面的响应头,包括本就安全的:
- Content-Language
- Content-Type
或者通过预检后被允许的:
- Cache-Control
- Expires
- Last-Modified
- Pragma
访问任何其他 response标头 都将导致 error。要授予 js 对任何其他响应标头的访问权限,服务器必须在响应中明确指定一个 Access-Control-Expose-Headers 标头,包含允许脚本得到的标头。
同源策略
和跨域请求建立在浏览器和服务器端的通信不同,同源策略限制了本地浏览器的窗口(window)之间的 js 通信。这个窗口包括了标签页(一个标签页对应一个 window)、Iframe(嵌入在页面中的窗口)。
同源策略规定:
- 两个同源的窗口(window对象)可以相互获得全部的访问权限。
- 否则就不能访问对方 window 对象下的如何东西,即使我们持有该 window 对象的引用。唯一的例外是
location:我们可以修改它(setter 被允许)。但不能访问它(getter 被禁止)。
开发中如何处理跨域
对于 js 请求:
最容易想到的,配置CORS头;
后端设置一层反向代理,后端是同源的,所以不会拦截;
用 html 标签
script请求服务器,浏览器并不会阻止 script 元素的请求,我们响应一段包含数据的 js 代码。这种做法被称为 “jsonp”。
对于 iframe:
postMessage(data, targetOrigin) // 发送方
window.addEventListener("message", (e) => {}) // 接收方跨域中如何携带 cookie
请求中携带一个标头 credentials: 'include' 将开启 credential 模式,该模式下允许携带 cookie 等敏感信息。响应也必须含有 Access-control-allow-credentials