好的,没问题! 咱们这就来聊聊 SpringMVC 对 CORS(跨域资源共享)的支持与配置,保证让你看完之后,不仅能彻底搞懂 CORS,还能轻松玩转 SpringMVC 的相关配置。
SpringMVC 与 CORS:一场“跨界”的恋爱
话说在互联网这个大Party上,各个网站就像一个个独立的舞池,为了安全起见,浏览器默认实施了“同源策略”,规定只有来自同一个“源”(协议、域名、端口号都相同)的脚本才能互相访问。这就好比舞池里设了门卫,只允许本舞池的人进出。
但是,现实中经常有这种需求:A舞池的客人想邀请B舞池的舞伴一起跳舞。这时候,CORS(Cross-Origin Resource Sharing,跨域资源共享)就闪亮登场了,它就像一个“通行证”,允许服务器告诉浏览器,哪些舞池的客人可以跨界来访问我的资源。
SpringMVC 作为一名优秀的“红娘”,自然也提供了强大的 CORS 支持,帮助你轻松配置服务器,实现不同舞池之间的友好互动。
为什么需要 CORS?
在深入 SpringMVC 的 CORS 配置之前,咱们先来搞清楚,什么情况下需要 CORS。
- 前后端分离架构: 如今流行的前后端分离架构中,前端项目(比如用 Vue、React、Angular 开发的)通常部署在独立的域名下,而后端 API 接口部署在另一个域名下。这时,前端项目通过 AJAX 请求后端 API,就会触发跨域问题。
- API 开放平台: 如果你的网站提供 API 接口,允许第三方网站调用,那么就需要配置 CORS,允许特定的第三方网站访问你的 API。
- 子域名访问: 即使是同一个主域名下的不同子域名,也属于不同的“源”,也会触发跨域问题。比如
app.example.com
和api.example.com
之间互相访问就需要 CORS。
CORS 的工作原理:预检请求与实际请求
CORS 并非直接允许跨域请求,而是通过一种“协商”机制来保证安全。这个机制分为两个阶段:
- 预检请求(Preflight Request): 当浏览器发现要发起一个“特殊”的跨域请求时,会先发送一个
OPTIONS
请求到服务器,询问服务器是否允许这个跨域请求。这个OPTIONS
请求就叫做预检请求。- 什么是“特殊”的跨域请求? 满足以下任一条件的跨域请求,都会被认为是“特殊”的:
- 请求方法不是
GET
、HEAD
或POST
。 POST
请求的Content-Type
不是application/x-www-form-urlencoded
、multipart/form-data
或text/plain
。- 请求头中包含除了浏览器默认允许的字段之外的自定义字段。
- 请求方法不是
- 什么是“特殊”的跨域请求? 满足以下任一条件的跨域请求,都会被认为是“特殊”的:
- 实际请求(Actual Request): 如果服务器在预检请求中返回允许跨域的响应头,浏览器才会发送真正的跨域请求。
SpringMVC 中的 CORS 配置:两种姿势
SpringMVC 提供了两种配置 CORS 的方式:
- 基于注解的配置(
@CrossOrigin
): 简单方便,适用于单个 Controller 或方法。 - 全局配置(
WebMvcConfigurer
): 灵活强大,适用于整个应用的 CORS 策略。
咱们先来看看基于注解的配置。
1. 基于注解的配置 (@CrossOrigin
)
@CrossOrigin
注解可以标注在 Controller 类或方法上,用于指定允许跨域访问的“源”。
-
标注在 Controller 类上:
@RestController @CrossOrigin(origins = "http://example.com") public class MyController { @GetMapping("/hello") public String hello() { return "Hello, CORS!"; } }
这段代码表示,只允许来自
http://example.com
的请求跨域访问MyController
中的所有接口。 -
标注在方法上:
@RestController public class MyController { @GetMapping("/hello") @CrossOrigin(origins = "http://example.com") public String hello() { return "Hello, CORS!"; } @GetMapping("/world") public String world() { return "World!"; } }
这段代码表示,只允许来自
http://example.com
的请求跨域访问/hello
接口,而/world
接口不允许跨域访问。
@CrossOrigin
注解的常用属性:
属性 | 描述 | 默认值 |
---|---|---|
origins |
允许跨域访问的“源”,可以是一个字符串,也可以是一个字符串数组。 | * (允许所有源) |
originPatterns |
允许跨域访问的“源”,可以使用 Ant 风格的通配符。 例如: https://*.example.com 。 |
空 |
methods |
允许的 HTTP 方法,可以是一个字符串数组。 | 允许所有方法 |
allowedHeaders |
允许的请求头,可以是一个字符串数组。 | 允许所有请求头 |
exposedHeaders |
允许浏览器访问的响应头,可以是一个字符串数组。默认情况下,浏览器只能访问一些标准的响应头,如果服务器返回了自定义的响应头,并且希望浏览器能够访问,就需要在这里指定。 | 空 |
allowCredentials |
是否允许发送 Cookie。 如果设置为 true , 那么 origins 不能设置为 * , 必须指定具体的域名。 因为 * 不安全。 |
false |
maxAge |
预检请求的缓存时间,单位是秒。 在这个时间内,浏览器不会再次发送预检请求。 | 1800 (30 分钟) |
示例:允许来自多个域名的跨域请求,并允许携带 Cookie
@RestController
@CrossOrigin(origins = {"http://example.com", "http://localhost:8080"}, allowCredentials = "true")
public class MyController {
@GetMapping("/hello")
public String hello() {
return "Hello, CORS!";
}
}
2. 全局配置 (WebMvcConfigurer
)
如果你需要对整个应用设置统一的 CORS 策略,可以使用 WebMvcConfigurer
接口。通过实现 WebMvcConfigurer
接口,你可以自定义 CORS 配置,并将其应用到所有的 Controller 中。
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class CorsConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**") // 匹配所有请求
.allowedOrigins("http://example.com", "http://localhost:8080") // 允许跨域的域名
.allowedMethods("GET", "POST", "PUT", "DELETE") // 允许的 HTTP 方法
.allowedHeaders("*") // 允许的请求头
.allowCredentials(true) // 允许携带 Cookie
.maxAge(3600); // 预检请求的缓存时间
}
}
代码解释:
@Configuration
:表示这是一个配置类,Spring 会自动加载它。WebMvcConfigurer
:实现了这个接口,就可以自定义 SpringMVC 的配置。addCorsMappings(CorsRegistry registry)
:这个方法用于配置 CORS 映射。registry.addMapping("/**")
:表示对所有请求都应用 CORS 策略。allowedOrigins("http://example.com", "http://localhost:8080")
:允许来自http://example.com
和http://localhost:8080
的跨域请求。allowedMethods("GET", "POST", "PUT", "DELETE")
:允许的 HTTP 方法。allowedHeaders("*")
:允许所有请求头。allowCredentials(true)
:允许携带 Cookie。maxAge(3600)
:预检请求的缓存时间为 3600 秒(1 小时)。
全局配置的优势:
- 集中管理: 所有的 CORS 策略都集中在一个地方配置,方便维护和修改。
- 避免重复配置: 不需要为每个 Controller 或方法都添加
@CrossOrigin
注解,减少了代码冗余。 - 更灵活的配置: 可以使用更复杂的匹配规则,例如根据请求路径或请求头来配置 CORS 策略。
两种配置方式的比较:
特性 | @CrossOrigin 注解 |
WebMvcConfigurer 全局配置 |
---|---|---|
适用范围 | 单个 Controller/方法 | 整个应用 |
配置方式 | 注解 | Java 代码 |
灵活性 | 较低 | 较高 |
维护性 | 较低 | 较高 |
如何选择:
- 如果只需要对少数几个接口进行 CORS 配置,可以使用
@CrossOrigin
注解,简单方便。 - 如果需要对整个应用设置统一的 CORS 策略,或者需要更灵活的配置,建议使用
WebMvcConfigurer
全局配置。
CORS 配置的注意事项:
allowCredentials
与origins
的关系: 如果allowCredentials
设置为true
,那么origins
不能设置为*
,必须指定具体的域名。因为*
不安全,可能导致 Cookie 被恶意网站窃取。- 预检请求的缓存时间: 预检请求的缓存时间越长,浏览器发送预检请求的次数就越少,可以提高性能。但是,如果服务器的 CORS 配置发生了变化,浏览器可能仍然使用缓存中的旧配置,导致跨域请求失败。因此,需要根据实际情况设置合适的缓存时间。
- 安全性: CORS 配置不当可能导致安全问题,例如允许恶意网站跨域访问你的 API,窃取用户数据。因此,在配置 CORS 时,一定要仔细考虑安全因素,只允许信任的域名跨域访问。
常见问题与解决方案:
-
预检请求失败:
- 问题原因: 预检请求的响应头中缺少必要的 CORS 字段,例如
Access-Control-Allow-Origin
、Access-Control-Allow-Methods
、Access-Control-Allow-Headers
。 - 解决方案: 检查服务器的 CORS 配置,确保返回了正确的响应头。可以使用浏览器的开发者工具查看预检请求的响应头。
- 问题原因: 预检请求的响应头中缺少必要的 CORS 字段,例如
-
实际请求失败:
- 问题原因: 实际请求的某些参数与预检请求不一致,例如请求头、请求方法。
- 解决方案: 确保实际请求与预检请求的参数一致。
-
Cookie 无法发送:
- 问题原因:
allowCredentials
没有设置为true
,或者origins
设置为*
。 - 解决方案: 将
allowCredentials
设置为true
,并将origins
设置为具体的域名。
- 问题原因:
-
自定义响应头无法访问:
- 问题原因: 响应头没有在
exposedHeaders
中指定。 - 解决方案: 将自定义响应头添加到
exposedHeaders
中。
- 问题原因: 响应头没有在
总结:
CORS 是解决跨域问题的关键技术,SpringMVC 提供了灵活方便的 CORS 配置方式。通过 @CrossOrigin
注解和 WebMvcConfigurer
全局配置,你可以轻松地控制哪些域名可以跨域访问你的 API,并保证安全性。记住,理解 CORS 的工作原理,仔细配置 CORS 策略,才能让你的网站在互联网的舞池中自由穿梭,与不同的舞伴愉快共舞!
希望这篇文章能让你对 SpringMVC 的 CORS 支持与配置有更深入的了解。 记住,实践是检验真理的唯一标准,多动手尝试,才能真正掌握 CORS 的奥秘!