跨域
同源策略:
- 协议、主机名、端口中有一个不同时,浏览器判定两者不同源,从而产生跨域
- 当前域下的 js 脚本不能够访问其他域下的 cookie、localStorage 和 indexDB。
- 当前域下的 js 脚本不能够操作访问操作其他域下的 DOM。
- 当前域下 ajax 无法发送跨域请求。
同源政策的目的主要是为了保证用户的信息安全,它只是对 js 脚本的一种限制,并不是对浏览器的限制,对于一般的 img、或者 script 脚本请求都不会有跨域的限制,这是因为这些操作都不会通过响应结果来进行可能出现安全问题的操作
允许跨域加载的资源
- `<img>`、`<script>`、`<link>`(如 CSS)的 `src`/`href` 属性。
- JSONP:通过 `<script>` 发送 GET 请求,利用回调函数接收数据(需服务端配合)。
- 浏览器允许直接建立跨域的 WebSocket 连接(但需服务端支持)。
跨域解决方案
CORS (Cross-Origin Resource Sharing) 校验规则
简单请求
简单请求:
- 请求方法:GET、HEAD、POST。
- 请求头:Accept、Accept-Language、Content-Language、Content-Type。
- Content-Type 仅支持:application/x-www-form-urlencoded、multipart/form-data、text/plain。
简单请求过程: 在简单请求中,在服务器内,至少需要设置字段:Access-Control-Allow-Origin
Access-Control-Allow-Credentials: true // 表示是否允许发送Cookie
Access-Control-Allow-Origin: <http://api.bob.com> // 和Orign一致
Access-Control-Expose-Headers: FooBar // 指定返回其他字段的值
Content-Type: text/html; charset=utf-8 // 表示文档类型
1
2
3
4
2
3
4
失败:如果 Orign 指定的域名不在许可范围之内,服务器会返回一个正常的 HTTP 回应,浏览器发现没有上面的 Access-Control-Allow-Origin 头部信息,就知道出错了。这个错误无法通过状态码识别,因为返回的状态码可能是 200。
成功:请求头部带上 Origin:域名,响应 Access-Control-Allow-Origin 等于这个域名或者 *
就通过校验
非简单请求
- 预检请求:options:请求带上 Origin 和方法和修改过的请求头
- 依赖服务端 / 后端在响应头中添加 Access-Control-Allow-* 头,告知浏览器端通过此请求
- Access-Control-Allow-Origin 表示允许的来源
- Access-Control-Allow-Methods 表示允许的请求方法
- Access-Control-Allow-Headers 表示允许的请求头
- Access-Control-Allow-Credentials 表示允许携带认证信息
- Access-Control-Max-Age: 1728000 用来指定本次预检请求的有效期,单位为秒
反向代理
- 通过同源的服务端对请求做一个转发处理,比如开发中通过工具配置 proxy 等
JSONP
- 只支持 GET 请求。利用了浏览器加载 JavaScript 资源文件时不受同源策略的限制而实现跨域获取数据
- 前端注册函数,服务端返回函数表达式,参数中携带数据
postMessage
- 即在两个 origin 下分别部署一套页面 A 与 B,A 页面通过 iframe 加载 B 页面并监听消息,B 页面发送消息
document.domain(手动修改,骗浏览器)
- 可将相同一级域名下的子域名页面的 document.domain 设置为一级域名实现跨域。
- 可将同域不同端口的 document.domain 设置为同域名实现跨域(端口被置为 null)。
nginx 代理
nginx
server {
listen 80;
location / {
root /var/www/html;
index index.html index.htm;
try_files $uri $uri/ /index.html;
}
location /api {
proxy_pass http://127.0.0.1:3000;
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
请求正常发出和响应,但是浏览器会对响应校验
在跨域请求时默认不会带上 cookie(会话级别):
- xhr.withCredentials = true,Access-Control-Allow-Credentials: true(服务器不得设置 Access-Control-Allow-Origin 的值为 *,设置为 * 带不上凭证,Access-Control-Allow-Headers 和 Access-Control-Allow-Methods 也不能为 *)
- 将 Cookie 的 SameSite 值设为 None,Secure 值改为 true,并且升级为 https,我们就可以 跨域 使用 Cookie。
- 将 Cookie 的 SameSite 值设为 Lax/Strict,并且将前后端部署在同一台服务器下,我们就可以在同一站点使用 Cookie。