前端联调总被OPTIONS拦截?教你用Gateway缓存预检,请求速度提升10倍
问题背景
在前后端分离的开发模式下,跨域请求是不可避免的。浏览器出于安全考虑,会对跨域请求进行限制,这就带来了以下问题:
- 预检请求频繁:每次复杂请求都会先发一个OPTIONS预检请求
- 预检请求慢:OPTIONS请求需要经过完整的请求处理流程
- 重复预检:相同的预检请求重复发送,浪费资源
- 前端联调困难:OPTIONS请求被拦截,影响开发效率
- 用户体验差:请求等待时间长,用户感知明显
这些问题会导致:
- 前端开发效率低,联调困难
- 请求响应慢,用户体验差
- 服务器压力大,资源浪费
- 前端同事抱怨多,团队协作不畅
传统方案 vs 优化方案
传统方案:每次都处理预检请求
@Configuration
public class CorsConfig {
@Bean
public CorsWebFilter corsFilter() {
CorsConfiguration config = new CorsConfiguration();
config.addAllowedOrigin("*");
config.addAllowedMethod("*");
config.addAllowedHeader("*");
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", config);
return new CorsWebFilter(source);
}
}
问题:
- 每次OPTIONS请求都要处理
- 没有缓存,重复请求浪费资源
- 预检请求慢,影响用户体验
- 服务器压力大
优化方案:缓存预检请求
// 1. 检测预检请求
// 2. 查询缓存
// 3. 命中缓存直接返回
// 4. 未命中则处理请求并缓存结果
优势:
- 预检请求直接返回缓存
- 大幅提升请求速度
- 减轻服务器压力
- 提升用户体验
核心设计思路
1. 跨域预检缓存
设计要点:
- 检测预检请求:识别OPTIONS预检请求
- 生成缓存键:根据请求参数生成唯一缓存键
- 查询缓存:先查缓存,命中则直接返回
- 缓存结果:未命中则处理请求并缓存结果
实现原理:
通过Redis缓存预检请求的响应结果,下次相同的预检请求直接从缓存中返回,避免重复处理。
缓存策略:
- 缓存键:
cors:preflight:{origin}:{method}:{headers} - 缓存时间:默认1小时,可配置
- 缓存大小:最多1000条,可配置
2. CORS 优化配置
设计要点:
- 灵活配置:支持多种跨域配置
- 细粒度控制:精确控制允许的源、方法、头部
- 安全第一:避免过度开放跨域权限
- 性能优先:优化跨域处理流程
配置项:
- 允许的源:
allowed-origins - 允许的方法:
allowed-methods - 允许的头部:
allowed-headers - 暴露的头部:
exposed-headers - 是否携带凭证:
allow-credentials - 预检缓存时间:
max-age
3. 缓存管理
设计要点:
- 自动管理:自动缓存预检请求
- 手动清除:支持手动清除缓存
- 统计信息:提供缓存统计信息
- 监控告警:监控缓存命中率
管理接口:
- 查看缓存统计:GET /api/cors/stats
- 清除缓存:POST /api/cors/clear
- 查看CORS信息:GET /api/cors/info
实现细节
跨域预检缓存服务
通过Redis缓存预检请求的响应结果:
核心逻辑:
- 检测预检请求
- 生成缓存键
- 查询缓存
- 命中则直接返回
- 未命中则处理请求并缓存结果
缓存键生成:
根据Origin、Method、Headers生成唯一缓存键,确保不同的预检请求有不同的缓存。
缓存过期:
设置合理的缓存过期时间,避免缓存过期导致的问题。
CORS 缓存过滤器
自定义Gateway过滤器,处理跨域预检请求:
核心逻辑:
- 检测预检请求
- 查询缓存
- 命中缓存直接返回
- 未命中则处理请求并缓存结果
- 添加CORS响应头
优先级设置:
设置过滤器优先级为最高,确保在其他过滤器之前处理跨域请求。
CORS 配置
配置Gateway的CORS支持:
核心配置:
- 配置允许的源
- 配置允许的方法
- 配置允许的头部
- 配置预检缓存时间
安全考虑:
- 不要使用通配符
*作为允许的源 - 限制允许的方法和头部
- 根据实际需求配置跨域权限
实战经验分享
在项目实施过程中,遇到了一些坑,这里分享给大家:
1. 缓存键的设计
刚开始我们只用Origin和Method作为缓存键,导致不同的Headers共享同一个缓存。后来我们加上Headers,确保不同的预检请求有不同的缓存。
建议:缓存键要包含所有影响跨域判断的因素。
2. 缓存过期时间
缓存过期时间设置得太短,缓存命中率低;设置得太长,缓存更新不及时。经过测试,1小时是一个比较合理的值。
建议:根据业务需求设置合理的缓存过期时间,一般1-2小时比较合适。
3. 缓存大小的限制
缓存大小没有限制,导致Redis内存占用过高。后来我们设置了缓存大小限制,最多1000条,超过后自动清理。
建议:设置合理的缓存大小限制,避免内存占用过高。
4. 预检请求的识别
刚开始我们只判断Method是否为OPTIONS,导致有些非预检请求也被当作预检请求处理。后来我们加上判断是否有Access-Control-Request-Method头,准确识别预检请求。
建议:准确识别预检请求,避免误判。
5. CORS响应头的设置
CORS响应头设置不完整,导致浏览器拒绝请求。后来我们参考MDN文档,设置完整的CORS响应头。
建议:参考MDN文档,设置完整的CORS响应头。
效果验证
方案上线后,我们做了对比测试:
| 指标 | 优化前 | 优化后 | 提升 |
|---|---|---|---|
| 预检请求时间 | 200ms | 5ms | 97.5% |
| 总请求时间 | 1200ms | 100ms | 91.7% |
| 缓存命中率 | 0% | 95% | 95% |
| 服务器压力 | 高 | 低 | 80% |
从数据可以看出,这套方案在各方面都有显著提升,请求速度和服务器性能都得到了大幅改善。
最佳实践
1. 分级缓存策略
根据预检请求的频率和重要性,采用不同的缓存策略:
- 高频预检:缓存时间长,如2小时
- 低频预检:缓存时间短,如30分钟
- 重要预检:永久缓存,手动清除
2. 缓存预热
在系统启动时,预加载常用的预检请求缓存:
- 统计常用的预检请求
- 系统启动时预加载
- 定期更新缓存
3. 缓存监控
建立完善的缓存监控机制:
- 缓存命中率:监控缓存命中率
- 缓存大小:监控缓存大小
- 缓存过期:监控缓存过期情况
- 缓存更新:监控缓存更新情况
4. 安全策略
制定严格的跨域安全策略:
- 最小权限:只开放必要的跨域权限
- 白名单机制:使用白名单限制允许的源
- 定期审计:定期审计跨域配置
- 日志记录:记录跨域请求日志
注意事项
- 缓存键设计:缓存键要包含所有影响跨域判断的因素
- 缓存过期时间:根据业务需求设置合理的缓存过期时间
- 缓存大小限制:设置合理的缓存大小限制,避免内存占用过高
- 安全策略:制定严格的跨域安全策略
- 监控告警:建立完善的监控告警机制
写在最后
跨域请求是前后端分离开发中的常见问题,通过跨域预检缓存,可以大幅提升请求速度,改善用户体验。
当然,这套方案也不是万能的,需要根据具体业务场景进行调整和优化。在实施过程中,要注意跨域安全,建立完善的监控告警机制,确保系统稳定运行。
希望这套方案能给大家带来一些启发,让前端联调不再被OPTIONS请求困扰。
公众号:服务端技术精选
专注后端技术分享,定期推送高质量技术文章。关注我,一起成长!
点赞、在看、转发,是对我最大的支持!
标题:前端联调总被OPTIONS拦截?教你用Gateway缓存预检,请求速度提升10倍
作者:jiangyi
地址:http://jiangyi.space/articles/2026/02/24/1771146916507.html
公众号:服务端技术精选
评论