跨域,总在发OPTIONS请求?这次终于搞懂CORS预检了

关注我的公众号:【编程朝花夕拾】,可获取首发内容。

01 引言

作为一名开发者,你是否经常在浏览器的 Network 面板里,看到一个神秘的 OPTIONS 请求总是抢在你真正的 API 请求之前?它来去匆匆,有时成功,有时失败,留下一个让人头疼的 CORS 错误便扬长而去。

你是否曾对此感到困惑:"这个多余的请求到底是谁发的?为什么要有它?我该怎么让它消失?" 别担心,你并不是一个人。这个看似多余的"预请求"(Preflight Request),其实是浏览器保护我们应用安全的关键"守门员"。

本文将带你彻底揭开 OPTIONS 请求的神秘面纱,从它的诞生原因到完美解决方案,让你不仅知其然,更知其所以然,从此轻松搞定跨域难题!

02 产生的原因

预请求OPTIONS的产生源于浏览器的同源策略,这是浏览器最基本的安全功能。同源策略限制了不同源之间的资源交互,防止恶意网站窃取数据。

然而,现代Web应用需要与不同域的API进行通信,这就产生了矛盾。尤其现在已经进入了移动时代,前后端分离是无法避开这样的问题:跨域CORS机制应运而生,在安全和功能之间取得平衡,也是解决跨域问题的关键技术。

跨域的前提下,并非所有请求都会触发预检。满足以下所有条件的请求属于 简单请求,不会触发预检:

  • 方法为:GET、HEAD、POST
  • 头信息仅包含:Accept, Accept-Language, Content-Language, Content-Type(值仅限于 application/x-www-form-urlencoded, multipart/form-data, text/plain
  • 请求中的任何 XMLHttpRequestUpload 对象均没有注册任何事件监听器。

反之,都称为复杂请求,如:

  • 使用了非简单方法:PUT、DELETE、PATCH 等。
  • 自定义请求头 :如AuthorizationX-token
  • Content-Type 超出简单范围application/json

这时,浏览器就会主动发起预请求 OPTIONS,以确保服务器理解和允许接下来的正式请求,从而保护服务器免受非法的跨域请求。

对于前端开发者而言,OPTIONS 请求是由浏览器自动处理的,无需编写额外代码。对于后端来说,预请求也不需要单独去编写方法,但是需要在CorsFilter中正确处理OPTIONS方法。

03 跨域的解决方案

跨域可以再网关层解决,如Nginx,也可以从服务端解决。我们从服务端的角度介绍几种方案。我们先用浏览器的一个HTML和服务模拟跨域请求。

通过自定义请求头,模拟复杂请求,触发预请求。

3.1 @CrossOrigin注解

这种方式是细粒度的控制每一个方法。

3.2 SpringBoot配置

这里要说的就是allowedOriginsallowedOriginPatterns的区别。测试的时候发现配置allowedOrigins("*")是不生效的。

  • allowedOrigins:只能配置固定的请求源,不能使用通配符
  • allowedOriginPatterns:就是allowedOrigins扩展,可以使用通配符,可以动态控制允许的源。

3.3 CorsFilter

全限定类名:org.springframework.web.filter.CorsFilter

配置基本一致,其中CorsConfiguration.ALL就是*

3.4 自定义过滤器

配置

java 复制代码
@Bean
public FilterRegistrationBean apiFilter() {
    FilterRegistrationBean registrationBean = new FilterRegistrationBean();
    registrationBean.setFilter(new ApiFilter());
    registrationBean.addUrlPatterns("/*");
    return registrationBean;
}

之前介绍的三种,预请求基本不会有问题,就算有问题也不好解决。自定义的跨域拦截器更加灵活。如果里面的尤其的业务逻辑,如果出现异常,就是导致预请求失败。这时我们需要特殊处理。增加如下的代码:

java 复制代码
String method = request.getMethod();
if (StringUtils.equalsIgnoreCase(method, "OPTIONS")) {
    response.setStatus(204);
    return;
}

表示只要是与请求,直接响应2xx,预请求就会通过。204表示响应成功,但是没有任何内容。

04 小结

总而言之,那个总是在你"非简单请求"前抢先一步的 OPTIONS 请求,并非多此一举,而是浏览器恪尽职守、严格执行安全策略的表现。

为了减少此类请求带来的资源消耗,我们可以通过设置Access-Control-Max-Age或者maxAge来控制指定时间段内读取缓存。

案例里面为了方便,放行了所有的请求方法、源以及请求头。在实际生产中为了安全,我们可以按需信任并放行指定的资源。

下次再在 Network 里看到它,你大可以会心一笑,因为你已经透彻理解了它背后的安全逻辑,并能轻松地驾驭它。从此,CORS 预检不再是拦路虎,而是你Web开发知识体系里一个牢固的组成部分。

相关推荐
少妇的美梦2 小时前
Spring Boot搭建MCP-SERVER,实现Cherry StudioMCP调用
后端·mcp
AI大模型2 小时前
大模型提示词开发:从“简单指令”到“工程化架构”的技术跃迁
程序员·llm·agent
AI大模型2 小时前
手把手教:LangChain+Qwen3搭建本地RAG问答系统,从0到1全流程
程序员·langchain·llm
这里有鱼汤2 小时前
如何用Python找到股票的支撑位和压力位?——均线簇
后端·python
考虑考虑2 小时前
dubbo3超时时间延长
java·后端·dubbo
刘立军2 小时前
本地大模型编程实战(36)使用知识图谱增强RAG(2)生成知识图谱
后端·架构
boy快快长大2 小时前
【Spring AI】简单入门(一)
java·人工智能·spring
失散133 小时前
分布式专题——15 ZooKeeper特性与节点数据类型详解
java·分布式·zookeeper·云原生·架构
ThisIsMirror3 小时前
Spring的三级缓存如何解决单例Bean循环依赖
java·spring·缓存