(一)同源策略
1. 什么是同源策略
- 同源策略是一种约定,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,浏览器很容易受到XSS、CSRF等攻击。
- 所谓同源是指 “ 协议+域名+端口 ” 三者相同,即便两个不同的域名指向同一个ip地址,也非同源。
- 形式:
协议://域名:(端口号)
- 形式:
2. 同源策略的限制内容
同源策略限制以下几种行为:
- Cookie、LocalStorage 和 IndexDB 无法读取
- DOM和JS对象无法获得
- AJAX 请求不能发送,被浏览器拦截了
但是有三个标签是允许跨域加载资源:
<img src=XXX><link href=XXX><script src=XXX>
(二)跨域
1. 跨域的原理
- 跨域,是指浏览器不能执行其他网站的脚本。它是由浏览器的
同源策略造成的。 - 跨域原理,即是通过各种方式,
避开浏览器的安全限制。
2. 跨域的方式与特点 ⭐⭐⭐⭐⭐
&-1. JSONP(JSON with Padding)⭐⭐⭐⭐⭐
(1)原理:
JSONP通过同源策略涉及不到的"漏洞",也就是像img中的src,link标签的href,script的src都不受同源策略的限制。- 利用这个特性,服务端不再返回 JSON 格式的数据,而是 返回一段调用某个函数的 js 代码,在 src 中进行了调用,这样实现了跨域。
(2)步骤:
- 创建一个script标签
- script的src属性设置接口地址
- 接口参数,必须要带一个自定义函数名(前后端约定好),要不然后台无法返回数据
- 通过自定义函数名去接受返回的数据
// 动态创建 script
var script = document.createElement('script');
script.setAttribute('type', 'text/javascript')
// 设置回调函数,用于后端返回的js代码中进行调用
function getData(data) {
console.log(data);
}
//设置 script 的 src 属性,并设置请求地址
script.src = 'http://localhost:3000/?callback=getData';
// 让 script 生效
document.body.appendChild(script); //将返回的文本插入页面执行,让script调用回调函数
(3)JSONP 的缺点:
JSON 只支持 get,因为 script 标签只能使用 get 请求; JSONP 需要后端配合返回指定格式的数据。
&-2. CORS⭐⭐⭐⭐⭐
- CORS:CORS是一个W3C标准,全称是”跨域资源共享”(Cross-origin resource sharing),允许浏览器向跨源服务器,发出XMLHttpRequest请求,从而克服了AJAX只能同源使用的限制。
- 服务器设置
Header[Access-Control-Allow-Origin]HTTP响应头之后,浏览器将会允许跨域请求
浏览器将CORS请求分成两类:简单请求和非简单请求
&-&-1. 简单请求
- 满足以下条件,就是简单请求:
- 请求方法是:
HEAD、POST、GET - 请求头只有:
Accept、AcceptLanguage、ContentType、ContentLanguage、Last-Event-Id
- 请求方法是:
- 简单请求,浏览器自动添加一个
Origin字段 - 同时后端需要设置的请求头:
CORS Header属性 解释 Access-Control-Allow-Origin (必须) 表示接受哪些域名的请求( *为所有)Access-Control-Expose-Headers(可选) 允许跨域请求包含(XMLHttpRequest只能拿到六个字段: Cache-Control、Content-Language、Content-Type(仅限于 application/x-www-form-urlencoded、multipart/form-data 或 text/plain)、Last-Modified、Expires、Pragma,
如果想拿到其他的需要使用该字段指定Access-Control-Allow-Credentials(可选) 设置是否允许传Cookie
(要是想传cookie,前端需要设置xhr.withCredentials = true,后端设置Access-Control-Allow-Credentials: true)- Access-Control-Expose-Headers的六个字段属性:
- ① Cache-Control:通过指定首部字段
Cache-Control的指令,来进行缓存操作的工作机制,多个参数之间可以使用“,”分隔 - ② Content-Language:会告知客户端,实体主体使用的自然语言(指中文或英文等语言);
- ③ Content-Type:说明实体主体内对象的媒体类型
- ④ Last-Modified:指明资源最终修改时间
- ⑤ Expires:会将资源失效日期告知客户端
- ⑥ Pragma:是HTTP/1.1 之前版本保留的历史遗留字段,仅作为与HTTP/1.0 的向后兼容而定义。
- ① Cache-Control:通过指定首部字段
- Access-Control-Expose-Headers的六个字段属性:
- Access-Control-Max-Age 设置在86400秒不需要再发送预校验请求
- Access-Control-Allow-Methods 设置允许跨域请求的方法
- 简单请求示例
GET /cors? HTTP/1.1 // 请求方法是:HEAD、POST、GET
Host: localhost:2333
Connection: keep-alive
Origin: http://localhost:2332 // 简单请求,浏览器自动添加一个`Origin`字段
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36 Accept: */*
Referer: http://localhost:2332/CORS.html
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9
If-None-Match: W/"1-NWoZK3kTsExUV00Ywo1G5jlUKKs"
- 后端响应
&-&-2. 非简单请求
非简单请求则是不满足上边的两种情况之一,比如请求的方式为
PUT,或者请求头包含其他的字段
步骤:预检请求 -> 预检请求通过 -> 正式请求
- 非简单请求的
CORS请求会在正式通信之前进行一次预检请求
预检请求格式OPTIONS:请求行 的请求方法为OPTIONS(专门用来询问的)Origin:通过预检之后的请求,会自动带上Origin字段Access-Control-Request-Method:请求的方式Access-Control-Request-Header
OPTIONS /cors HTTP/1.1 //`"预检"`使用的请求方法是 `OPTIONS` , 表示这个请求使用来询问的 Origin: localhost:2333 Access-Control-Request-Method: PUT // 表示使用的什么HTTP请求方法 Access-Control-Request-Headers: X-Custom-Header // 表示浏览器发送的自定义字段 Host: localhost:2332 Accept-Language: zh-CN,zh;q=0.9 Connection: keep-alive User-Agent: Mozilla/5.0... - 预检请求后,服务器收到预检请求以后,检查了
Origin、Access-Control-Request-Method和Access-Control-Request-Headers字段以后,确认允许跨源请求,就可以做出回应。- 预检的响应头:
HTTP/1.1 200 OK Date: Mon, 01 Dec 2008 01:15:39 GMT Server: Apache/2.0.61 (Unix) Access-Control-Allow-Origin: http://localhost:2332 // 表示http://localhost:2332可以访问数据 Access-Control-Allow-Methods: GET, POST, PUT Access-Control-Allow-Headers: X-Custom-Header Content-Type: text/html; charset=utf-8 Content-Encoding: gzip Content-Length: 0 Keep-Alive: timeout=2, max=100 Connection: Keep-Alive Content-Type: text/plain - 通过预检后,才会进行正式的请求,浏览器接下来的每次请求就类似于简单请求了
- 一旦通过了预检请求后,请求的时候就会跟简单请求一样,会有一个
Origin头信息字段。 - 通过预检之后的,浏览器发出正式请求:
PUT /cors HTTP/1.1 Origin: http://api.bob.com // 通过预检之后的请求,会自动带上Origin字段 Host: api.alice.com X-Custom-Header: value Accept-Language: en-US Connection: keep-alive User-Agent: Mozilla/5.0... - 一旦通过了预检请求后,请求的时候就会跟简单请求一样,会有一个
&-3. document.domain
基础域名相同 子域名不同
&-4. window.name
利用在一个浏览器窗口内,载入所有的域名都是共享一个window.name
&-5. proxy代理
目前常用方式,通过服务器设置代理
&-6. window.postMessage()
利用h5新特性window.postMessage()
参考链接