SpringMVC 对 CORS(跨域资源共享)的支持与配置

好的,没问题! 咱们这就来聊聊 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.comapi.example.com 之间互相访问就需要 CORS。

CORS 的工作原理:预检请求与实际请求

CORS 并非直接允许跨域请求,而是通过一种“协商”机制来保证安全。这个机制分为两个阶段:

  1. 预检请求(Preflight Request): 当浏览器发现要发起一个“特殊”的跨域请求时,会先发送一个 OPTIONS 请求到服务器,询问服务器是否允许这个跨域请求。这个 OPTIONS 请求就叫做预检请求。
    • 什么是“特殊”的跨域请求? 满足以下任一条件的跨域请求,都会被认为是“特殊”的:
      • 请求方法不是 GETHEADPOST
      • POST 请求的 Content-Type 不是 application/x-www-form-urlencodedmultipart/form-datatext/plain
      • 请求头中包含除了浏览器默认允许的字段之外的自定义字段。
  2. 实际请求(Actual Request): 如果服务器在预检请求中返回允许跨域的响应头,浏览器才会发送真正的跨域请求。

SpringMVC 中的 CORS 配置:两种姿势

SpringMVC 提供了两种配置 CORS 的方式:

  1. 基于注解的配置(@CrossOrigin): 简单方便,适用于单个 Controller 或方法。
  2. 全局配置(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.comhttp://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 配置的注意事项:

  • allowCredentialsorigins 的关系: 如果 allowCredentials 设置为 true,那么 origins 不能设置为 *,必须指定具体的域名。因为 * 不安全,可能导致 Cookie 被恶意网站窃取。
  • 预检请求的缓存时间: 预检请求的缓存时间越长,浏览器发送预检请求的次数就越少,可以提高性能。但是,如果服务器的 CORS 配置发生了变化,浏览器可能仍然使用缓存中的旧配置,导致跨域请求失败。因此,需要根据实际情况设置合适的缓存时间。
  • 安全性: CORS 配置不当可能导致安全问题,例如允许恶意网站跨域访问你的 API,窃取用户数据。因此,在配置 CORS 时,一定要仔细考虑安全因素,只允许信任的域名跨域访问。

常见问题与解决方案:

  1. 预检请求失败:

    • 问题原因: 预检请求的响应头中缺少必要的 CORS 字段,例如 Access-Control-Allow-OriginAccess-Control-Allow-MethodsAccess-Control-Allow-Headers
    • 解决方案: 检查服务器的 CORS 配置,确保返回了正确的响应头。可以使用浏览器的开发者工具查看预检请求的响应头。
  2. 实际请求失败:

    • 问题原因: 实际请求的某些参数与预检请求不一致,例如请求头、请求方法。
    • 解决方案: 确保实际请求与预检请求的参数一致。
  3. Cookie 无法发送:

    • 问题原因: allowCredentials 没有设置为 true,或者 origins 设置为 *
    • 解决方案:allowCredentials 设置为 true,并将 origins 设置为具体的域名。
  4. 自定义响应头无法访问:

    • 问题原因: 响应头没有在 exposedHeaders 中指定。
    • 解决方案: 将自定义响应头添加到 exposedHeaders 中。

总结:

CORS 是解决跨域问题的关键技术,SpringMVC 提供了灵活方便的 CORS 配置方式。通过 @CrossOrigin 注解和 WebMvcConfigurer 全局配置,你可以轻松地控制哪些域名可以跨域访问你的 API,并保证安全性。记住,理解 CORS 的工作原理,仔细配置 CORS 策略,才能让你的网站在互联网的舞池中自由穿梭,与不同的舞伴愉快共舞!

希望这篇文章能让你对 SpringMVC 的 CORS 支持与配置有更深入的了解。 记住,实践是检验真理的唯一标准,多动手尝试,才能真正掌握 CORS 的奥秘!

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注