前言
本文谈到的前端预检请求其实就是解决跨域方案其中之一的安全机制,可以理解为你想进一个小区,门口有一个保安“拦截器”,通过了保安“拦截器”检查,就可以进入了。
关于跨域(端口、协议、域名),想必大家都不陌生吧,回想下跨域的相关知识以及解决方案,就知道前端预检请求的来源了。
知其然知其所以然
一、浏览器的同源策略
跨域问题主要源于浏览器的同源策略(Same-Origin Policy) 。该策略是浏览器最核心的安全机制之一,用于防止不同源之间的恶意行为。
同源的定义:
当一个请求的 URL 的协议、域名、端口三者中任意一个与当前页面的 URL 不同时,就称为跨域请求。
例如:
- 协议不同:
http://example.com和https://example.com - 域名不同:
http://www.example.com和http://www.other.com - 端口不同:
http://example.com:8080和http://example.com:8081
即使两个域名指向同一个 IP 地址,也属于跨域。
由于浏览器出于安全考虑,同源策略限制了以下行为:
- 无法读取非同源网页的 Cookie、LocalStorage 和 IndexedDB;
- 无法访问非同源网页的 DOM;
- 无法向非同源地址发送 AJAX 请求。
二、解决跨域的方法
1. CORS(跨域资源共享)
CORS 是目前最常用且推荐的跨域解决方案。它通过在服务器端设置响应头来允许特定源访问资源。
- 服务器通过设置
Access-Control-Allow-Origin响应头来指定允许访问的源。 - 可以设置为具体域名或
*(表示允许所有源)。 - 支持所有类型的
HTTP请求,功能完善。
2. JSONP(JSON with Padding)
JSONP 是一种利用 <script> 标签不受同源策略限制的特性实现跨域请求的方式。
- 仅支持 GET 请求;
- 存在安全风险,容易受到 XSS 攻击;
- 目前已被 CORS 取代。
3. 代理服务器(正向/反向代理)
通过在本地搭建一个代理服务器,前端请求先发送到代理服务器,再由代理服务器转发到目标服务器。
- 在开发环境中常用前端脚手架配置代理;
- 生产环境则可以使用
Nginx等反向代理工具。
4. WebSocket
WebSocket 协议不遵循同源策略,适用于需要实时通信的场景。
5. postMessage API
用于不同窗口或 iframe 之间传递消息,常用于跨域通信。
6. document.domain + iframe
适用于主域名相同但子域名不同的情况。
7. window.name + iframe
通过 iframe 的 window.name 属性实现跨域数据传递。
前端预检请求是什么?
前端预检请求(Preflight Request)是浏览器在发起某些跨域请求前,自动发送的一个 OPTIONS 请求,用于确认服务器是否允许实际的跨域请求。这种机制是为了保证安全性,防止未经允许的跨域请求对服务器数据造成影响。
CORS(跨域资源共享)机制在特定条件下会触发预检请求(Preflight Request) 。
注意:如果请求满足“简单请求”的所有条件(如使用 GET、POST、HEAD 方法,且 Content-Type 为 application/x-www-form-urlencoded、multipart/form-data 或 text/plain),则不会触发预检请求。
何时会触发预检请求?
当请求不满足“简单请求”的条件时,浏览器就会自动触发一个预检请求。简单请求包括以下几种情况:
- 使用 GET、HEAD 或 POST 方法;
- 请求头仅包含以下字段:
Accept、Accept-Language、Content-Language、Content-Type(且值为text/plain、multipart/form-data或application/x-www-form-urlencoded)。
如果请求中包含以下任意一种情况,则会被视为“非简单请求”,从而触发预检请求:
- 使用了非简单 HTTP 方法,如 PUT、DELETE、PATCH 等;
- 请求头中包含了自定义字段,例如
Authorization、X-Custom-Header等; Content-Type设置为application/json、application/xml等非简单类型;- 请求中携带了凭证(如
Cookie),需设置withCredentials = true。
预检请求的内容
预检请求是一个 OPTIONS 方法的请求,它会携带以下关键请求头:
Access-Control-Request-Method:表示实际请求将使用的 HTTP 方法;Access-Control-Request-Headers:列出实际请求中使用的自定义头部。
服务器如何响应预检请求?
服务器需要返回一系列 CORS 响应头来表明其是否允许该跨域请求:
Access-Control-Allow-Origin:指定允许访问的源;Access-Control-Allow-Methods:列出允许的HTTP方法;Access-Control-Allow-Headers:声明允许的请求头部;Access-Control-Allow-Credentials:是否允许携带凭证(如Cookie);Access-Control-Max-Age:指定预检请求结果的缓存时间,减少重复预检。
为什么需要预检请求?
预检请求本质上是一种安全机制,确保服务器明确知道并同意来自某个源的请求。这可以避免一些潜在的安全风险,比如在未授权的情况下向服务器发送敏感操作。
如何优化预检请求?
预检请求是现代浏览器为保障跨域请求安全而设计的一种机制,虽然会带来额外的网络开销,但在必要时是不可或缺的。
站在开发者角度,性能优化还是少不了的。全面认识了预检请求,优化方案减少必要自己也就出来了。为什么平时开发项目大部分是简单请求GET、HEAD 或 POST?这个问题答案也自己出来了吧。
总结下为了减少不必要的预检请求,可以采取以下策略:
- 尽量使用简单请求:避免使用非标准方法或自定义头部;
- **合理设置
Access-Control-Max-Age**:通过设置较长的缓存时间,减少重复的 OPTIONS 请求; - 使用代理服务器:将跨域请求转发到同源接口,绕过浏览器的 CORS 检查。
本文思维导图
写在最后
我是凉城a,一个前端,热爱技术也热爱生活。
与你相逢,我很开心。
- 文中如有错误,欢迎在评论区指正,如果这篇文章帮到了你,欢迎点赞和关注😊
- 本文首发于掘金,未经许可禁止转载💌