845 字
4 分钟
Spring Boot跨域问题

问题描述#

当前端发起一个请求,这个请求的接口满足跨协议、跨域名、跨端口任意一个时候都会触发浏览器的同源策略(Same-Origin Policy),其核心目的是防止恶意网站通过脚本(如 JavaScript)非法访问其他域的资源(如数据、Cookie 等),从而保护用户隐私和数据安全。

同源#

两个 URL协议(Protocol)、域名(Domain)、端口(Port) 必须完全一致,才属于同源。

https://example.com/page1https://example.com/page2 ✅ 同源(协议、域名、端口一致)
http://example.comhttps://example.com ❌ 不同源(协议不同)
example.com 和 sub.example.com ❌ 不同源(域名不同)
example.com:80 和 example.com:8080 ❌ 不同源(端口不同)

触发场景#

当浏览器通过 JavaScript 发起以下类型的请求时,若目标 URL 与当前页面不同源,就会触发跨域限制:

  1. AJAX/Fetch 请求(如 XMLHttpRequestfetch()
  2. WebSocket 请求
  3. 跨域资源嵌入(如 <script><img><link> 标签加载外部资源时,浏览器允许加载但会限制 JavaScript 读取内容)
  4. 跨域 DOM 操作(如 iframe 嵌套不同源页面时的通信)

跨域问题原理#

浏览器的同源策略通过以下机制实现:

  1. 请求可以发送,但响应被拦截 浏览器允许发送跨域请求,但会检查服务器的响应头中是否包含允许跨域的标记(如 Access-Control-Allow-Origin)。如果未通过检查,浏览器会阻止 JavaScript 读取响应内容。

  2. 预检请求(Preflight Request) 对于可能对服务器数据产生副作用的复杂请求(如 PUTDELETE 或自定义请求头),浏览器会先发送一个 OPTIONS 请求(预检请求),询问服务器是否允许实际请求。只有服务器明确授权后,浏览器才会发送真正的请求。

示例流程:

浏览器 → 发送 OPTIONS 预检请求 → 服务器
浏览器 ← 检查响应头(如 Access-Control-Allow-Methods) ← 服务器
(如果通过)浏览器 → 发送真实请求(如 POST) → 服务器

Cookie、LocalStorage 等存储数据默认不可跨域访问。
iframe 中的跨域页面无法通过 JavaScript 直接操作父页面或子页面的 DOM。

解决方案#

JSONP (仅限 GET 请求)#

前端:

$.ajax({
    url: 'http://localhost:8080/info?id=666',
    type: 'GET',
    dataType: 'jsonp',  // 期望从服务器返回的数据类型
    jsonp: 'lettle',    // 指定参数名
    success: function(resoponse) {
        alert(response.data);
    }
})

后端:

@RequestMapping(value="/info", method = RequestMethod)
@ResponseBody
public JSONPObject Info(@RequestParam("id") String id, String lettle) {
    Info info = infoService.selectInfoById(id);
    return new JSONPObject(lettle, info);
}

服务端改动#

前端:

$.ajax({
    url: 'http://localhost:8080/info?id=666',
    type: 'GET',
    dataType: 'json',  // 期望从服务器返回的数据类型
    success: function(resoponse) {
        alert(response.data);
    }
})

后端:

@RequestMapping(value="/info", method = RequestMethod)
@ResponseBody
@CrossOrigin("http://localhost:8090")
public Info Info(@RequestParam("id") String id, String lettle) {
    Info info = infoService.selectInfoById(id);
    return info;
}

在方法或者控制类上面使用@CrossOrigin注解,还能指定哪个端口

Config#

@Configuration
public class MyConfig implements WebMvcConfigurer {
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/info")                        // 哪些接口
                .allowedOrigins("http://localhost:8090/")   // 允许哪些域名
                .allowedMethods("GET");                     // 访问类型
    }
}

或者自定义 Filter 手动设置响应头:

@Configuration
public class MyConfig2 {
    @Bean
    public Filter corsFilter() {
        return new Filter() {
            @Override
            public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) 
                throws IOException, ServletException {
                HttpServletResponse response = (HttpServletResponse) res;
                response.setHeader("Access-Control-Allow-Origin", "http://localhost:3000");
                response.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
                response.setHeader("Access-Control-Max-Age", "3600");
                response.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization");
                response.setHeader("Access-Control-Allow-Credentials", "true");
                chain.doFilter(req, res);
            }

            // 其他方法空实现即可
            @Override public void init(FilterConfig filterConfig) {}
            @Override public void destroy() {}
        };
    }
}

Nginx#

在Nginx里设置一些跨域响应头

add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, DELETE, PUT';
add_header 'Access-Control-Allow-Headers' 'Content-Type, Authorization';
add_header 'Access-Control-Allow-Origin' 1728000;
Spring Boot跨域问题
https://fuwari.vercel.app/posts/cors/
作者
Lettle
发布于
2025-02-24
许可协议
CC BY-NC-SA 4.0