前端联调总被OPTIONS拦截?教你用Gateway缓存预检,请求速度提升10倍

问题背景

在前后端分离的开发模式下,跨域请求是不可避免的。浏览器出于安全考虑,会对跨域请求进行限制,这就带来了以下问题:

  1. 预检请求频繁:每次复杂请求都会先发一个OPTIONS预检请求
  2. 预检请求慢:OPTIONS请求需要经过完整的请求处理流程
  3. 重复预检:相同的预检请求重复发送,浪费资源
  4. 前端联调困难:OPTIONS请求被拦截,影响开发效率
  5. 用户体验差:请求等待时间长,用户感知明显

这些问题会导致:

  • 前端开发效率低,联调困难
  • 请求响应慢,用户体验差
  • 服务器压力大,资源浪费
  • 前端同事抱怨多,团队协作不畅

传统方案 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缓存预检请求的响应结果:

核心逻辑

  1. 检测预检请求
  2. 生成缓存键
  3. 查询缓存
  4. 命中则直接返回
  5. 未命中则处理请求并缓存结果

缓存键生成
根据Origin、Method、Headers生成唯一缓存键,确保不同的预检请求有不同的缓存。

缓存过期
设置合理的缓存过期时间,避免缓存过期导致的问题。

CORS 缓存过滤器

自定义Gateway过滤器,处理跨域预检请求:

核心逻辑

  1. 检测预检请求
  2. 查询缓存
  3. 命中缓存直接返回
  4. 未命中则处理请求并缓存结果
  5. 添加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响应头。

效果验证

方案上线后,我们做了对比测试:

指标优化前优化后提升
预检请求时间200ms5ms97.5%
总请求时间1200ms100ms91.7%
缓存命中率0%95%95%
服务器压力80%

从数据可以看出,这套方案在各方面都有显著提升,请求速度和服务器性能都得到了大幅改善。

最佳实践

1. 分级缓存策略

根据预检请求的频率和重要性,采用不同的缓存策略:

  • 高频预检:缓存时间长,如2小时
  • 低频预检:缓存时间短,如30分钟
  • 重要预检:永久缓存,手动清除

2. 缓存预热

在系统启动时,预加载常用的预检请求缓存:

  • 统计常用的预检请求
  • 系统启动时预加载
  • 定期更新缓存

3. 缓存监控

建立完善的缓存监控机制:

  • 缓存命中率:监控缓存命中率
  • 缓存大小:监控缓存大小
  • 缓存过期:监控缓存过期情况
  • 缓存更新:监控缓存更新情况

4. 安全策略

制定严格的跨域安全策略:

  • 最小权限:只开放必要的跨域权限
  • 白名单机制:使用白名单限制允许的源
  • 定期审计:定期审计跨域配置
  • 日志记录:记录跨域请求日志

注意事项

  1. 缓存键设计:缓存键要包含所有影响跨域判断的因素
  2. 缓存过期时间:根据业务需求设置合理的缓存过期时间
  3. 缓存大小限制:设置合理的缓存大小限制,避免内存占用过高
  4. 安全策略:制定严格的跨域安全策略
  5. 监控告警:建立完善的监控告警机制

写在最后

跨域请求是前后端分离开发中的常见问题,通过跨域预检缓存,可以大幅提升请求速度,改善用户体验。

当然,这套方案也不是万能的,需要根据具体业务场景进行调整和优化。在实施过程中,要注意跨域安全,建立完善的监控告警机制,确保系统稳定运行。

希望这套方案能给大家带来一些启发,让前端联调不再被OPTIONS请求困扰。


公众号:服务端技术精选

专注后端技术分享,定期推送高质量技术文章。关注我,一起成长!

点赞、在看、转发,是对我最大的支持!


标题:前端联调总被OPTIONS拦截?教你用Gateway缓存预检,请求速度提升10倍
作者:jiangyi
地址:http://jiangyi.space/articles/2026/02/24/1771146916507.html
公众号:服务端技术精选
    评论
    0 评论
avatar

取消