spring|spring web|跨域问题
基于spring-boot 4.0.x
跨域
浏览器的安全规则:同源策略 (Same‑Origin Policy),防止 CSRF 安全攻击
同源的判断标准:三者必须全部相同
- 协议 + 域名(IP) + 端口,有任意一个不一样 → 不同源 = 跨域
| 前端页面地址 | 后端接口地址 | 是否跨域 | 原因 |
|---|---|---|---|
| http://localhost:5173 | http://localhost:8080 | ✅ 跨域 | 端口不一样 |
| http://a.com | https://a.com | ✅ 跨域 | 协议 http/https 不一样 |
| http://a.com | http://b.com | ✅ 跨域 | 域名不一样 |
| http://127.0.0.1:5173 | http://localhost:8080 | ✅ 跨域 | IP / 域名不同 |
| http://a.com:8080/api | http://a.com:8080/user | ❌ 不跨域 | 协议、域名、端口全部一致,只是路径不同 |
CORS
- CORS = Cross‑Origin Resource Sharing 跨域资源共享
- 就是后端告诉浏览器:这个来源的前端是合法的,请放行,允许 JS 读取返回结果
后端返回特殊响应头给浏览器
Access‑Control‑Allow‑Origin: http://localhost:5173 #允许哪个前端来源
Access‑Control‑Allow‑Credentials: true #是否允许cookie/token凭证
Access‑Control‑Allow‑Methods: GET,POST,PUT... #允许哪些请求方法
OPTIONS预检请求
当前端是复杂请求(带 cookie、自定义 header,如 token),浏览器不会直接发 POST,先发一次
OPTIONS请求询问后端:“允许这个跨域请求吗?”
源码
https://gitee.com/jysemel-spring/spring-boot/tree/master/action/action-web-sample/action-web-sample-cors
pom依赖
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webmvc</artifactId>
</dependency>
<!-- AOP 切面依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aspectj</artifactId>
</dependency>
</dependencies>
部分代码
全局
package com.jysemel.spring.boot.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/global")
public class GlobalController {
@GetMapping("/test")
public String test() {
System.out.println("【业务代码】Controller业务逻辑执行");
return "success";
}
}
package com.jysemel.spring.boot.interceptor;
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 WebConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
//指定 global 路径前缀 全局放开
registry.addMapping("/global/**")
.allowedOriginPatterns("*")
.allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
.allowedHeaders("*")
.allowCredentials(true)
.maxAge(3600);
}
}
指定接口
package com.jysemel.spring.boot.controller;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/cross")
@CrossOrigin(
originPatterns = "*",
allowCredentials = "true",
allowedHeaders = "*",
methods = {RequestMethod.GET, RequestMethod.POST},
maxAge = 3600
)
public class CrossController {
@GetMapping("/test")
public String test() {
System.out.println("【业务代码】/cross/test 业务逻辑执行");
return "success";
}
@GetMapping("/options-demo")
public String optionsDemoGet() {
System.out.println("【业务代码】/cross/options‑demo GET业务执行");
return "options‑demo‑get‑ok";
}
// Spring7强制:该path必须存在OPTIONS映射,@CrossOrigin注解才会对OPTIONS生效
@RequestMapping(value = "/options-demo", method = RequestMethod.OPTIONS)
public void preflightOptions() {
//空实现,仅占位,不会执行业务
}
}
验证结果



