Springboot如何解决跨域问题?

Springboot如何解决跨域问题?

Springboot如何解决跨域问题?

在前后端分离架构中,跨域问题是Java开发者绕不开的"基础门槛"。本文从跨域的本质(同源策略)出发,系统讲解SpringBoot中5种主流跨域方案 ,每个方案包含原理解析+可运行代码+适用场景,帮你彻底解决跨域问题。

一、先搞懂:为什么会有跨域?

跨域的根源是浏览器的同源策略(Same-Origin Policy)------这是浏览器的安全机制,要求请求的"源"必须与当前页面的"源"完全一致,才允许交互。也就是说在两个后端之间互相调用接口是没有跨域问题的。

1.1 同源的定义

"源"由三部分组成,三者完全相同才叫"同源":

  • 协议(如http/https
  • 域名(如localhost/www.xxx.com
  • 端口(如8080/8090

示例 :当前页面是http://localhost:8080,以下请求会被判定为跨域:

  • https://localhost:8080(协议不同)
  • http://127.0.0.1:8080(域名不同)
  • http://localhost:8090(端口不同)

1.2 跨域的表现

浏览器会拦截跨域请求,控制台抛出类似错误:

复制代码
Access to XMLHttpRequest at 'http://localhost:8081/api' from origin 'http://localhost:8080' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

二、方案1:JSONP(基本不用)

JSONP是早期跨域方案,前端后端需要同时更改,需要增加一个callback请求参数,当然这个callback参数也可以自定义。

2.1 原理

  1. 前端通过<script>标签请求后端接口,传递回调函数名(如callback=handleData);
  2. 后端将数据包裹在回调函数中返回(如handleData({"msg":"success"}));
  3. 前端提前定义回调函数,接收并处理数据。

2.2 实战代码

后端接口
java 复制代码
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class JsonpController {
    /**
     * JSONP接口:接收前端传递的callback参数,返回包裹后的JSON
     */
    @GetMapping("/info")
    public String jsonpInfo(@RequestParam String callback) {
        // 模拟返回的数据(比如根据id=16查询到的信息)
        String data = "{\"code\":200,\"msg\":\"success\",\"data\":{\"id\":16,\"name\":\"测试数据\",\"desc\":\"JSONP跨域示例\"}}";
        // 拼接JSONP格式的响应(回调函数名 + 数据)
        return callback + "(" + data + ")";
    }
}
前端测试
html 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>jQuery Ajax JSONP跨域示例</title>
    <!-- 引入jQuery -->
    <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.4/jquery.min.js"></script>
</head>
<body>
<button id="submitBtn">发起JSONP请求</button>

<script>
    $("#submitBtn").click(function () {
        $.ajax({
            url: 'http://localhost:8080/info?id=16', // 后端接口地址(带参数id=16)
            type: 'GET', // JSONP仅支持GET请求
            dataType: 'jsonp', // 关键:指定数据类型为jsonp
            jsonp: 'callback', // 告诉后端:前端用callback参数传递回调函数名(默认就是callback,可省略)
            success: function (response) {
                // 成功接收后端返回的数据
                console.log("JSONP请求成功,返回数据:", response);
                alert("请求成功!数据ID:" + response.data.id + ",名称:" + response.data.name);
            },
            error: function (err) {
                console.error("JSONP请求失败:", err);
                alert("请求失败,请检查控制台");
            }
        });
    });
</script>
</body>
</html>

2.3 适用场景

仅适用于简单GET请求,或需要兼容老旧浏览器(如IE)的场景,不推荐现代项目使用。

三、方案2:全局配置(WebMvcConfigurer)

这是SpringBoot中最常用、最优雅的跨域方案,通过配置全局CORS规则实现。

3.1 原理

基于W3C的CORS标准,服务端通过响应头告知浏览器"允许哪些源的请求",核心响应头包括:

  • Access-Control-Allow-Origin:允许的跨域源;
  • Access-Control-Allow-Methods:允许的请求方法;
  • Access-Control-Allow-Credentials:是否允许携带Cookie。

3.2 实战代码

全局配置类
java 复制代码
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 GlobalCorsConfig implements WebMvcConfigurer {
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**") // 匹配所有接口
                .allowedOriginPatterns("*") // 允许所有源(生产环境建议指定域名)
                .allowedMethods("GET", "POST", "PUT", "DELETE") // 允许的请求方法
                .allowedHeaders("*") // 允许所有请求头
                .allowCredentials(true) // 允许携带Cookie
                .maxAge(3600); // 预检请求缓存1小时
    }
}
测试接口
java 复制代码
@RestController
public class TestController {
    @GetMapping("/api/test/get")
    public String testGet() {
        return "GET跨域成功";
    }

    @PostMapping("/api/test/post")
    public String testPost(@RequestBody String data) {
        return "POST跨域成功,接收:" + data;
    }
}
前端测试(Axios)
html 复制代码
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<button onclick="axios.get('http://localhost:8080/api/test/get').then(res=>alert(res.data))">测试GET</button>
<button onclick="axios.post('http://localhost:8080/api/test/post', {name:'张三'}).then(res=>alert(res.data))">测试POST</button>

3.3 适用场景

前后端分离的现代项目,推荐作为默认方案使用。

四、方案3:CorsFilter(官方Filter方案)

通过Spring官方的CorsFilter实现跨域,比手动写Filter更规范、更灵活。

4.1 原理

CorsFilter是Spring封装的CORS过滤器,通过CorsConfiguration配置规则,再绑定到指定路径,自动拦截请求并添加跨域响应头。

4.2 实战代码

配置类
java 复制代码
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;

import java.util.Collections;

@Configuration
public class CorsFilterConfig {
    @Bean
    public CorsFilter corsFilter() {
        // 1. 配置跨域规则
        CorsConfiguration config = new CorsConfiguration();
        config.setAllowedOriginPatterns(Collections.singletonList("*"));
        config.addAllowedHeader(CorsConfiguration.ALL);
        config.addAllowedMethod(CorsConfiguration.ALL);
        config.setAllowCredentials(true);
        config.setMaxAge(3600L);

        // 2. 绑定路径
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", config); // 所有路径应用规则

        // 3. 返回CorsFilter
        return new CorsFilter(source);
    }
}

4.3 适用场景

需要更灵活的跨域逻辑(如动态调整跨域规则),或项目中已使用Filter链的场景。

五、方案4:局部配置(@CrossOrigin注解)

仅为个别接口/Controller配置跨域,优先级高于全局配置。

5.1 原理

@CrossOrigin是Spring提供的注解,底层基于CORS机制,为标注的接口自动添加跨域响应头。

5.2 实战代码

单个接口配置
java 复制代码
@RestController
public class CrossOriginController {
    @CrossOrigin(origins = "*", maxAge = 3600)
    @GetMapping("/api/cross/test")
    public String crossTest() {
        return "单个接口跨域成功";
    }
}
Controller级配置
java 复制代码
@CrossOrigin(origins = "http://localhost:8080") // 仅允许8080源
@RestController
public class CrossOriginController2 {
    @GetMapping("/api/cross/test2")
    public String crossTest2() {
        return "Controller级跨域成功";
    }
}

5.3 适用场景

个别接口需要单独配置跨域的场景,不推荐全局使用。

六、方案对比与选型建议

方案 适用场景 优点 缺点
JSONP 简单GET请求、兼容老旧浏览器 实现简单 仅支持GET、有安全风险
WebMvcConfigurer 前后端分离全局跨域 优雅简洁、易维护 灵活性稍弱
CorsFilter 复杂跨域规则、Filter链场景 灵活可控、官方规范 代码量略多
@CrossOrigin 个别接口/Controller跨域 局部配置、无需全局修改 不适合全局场景
相关推荐
shepherd1265 分钟前
深度剖析SkyWalking:从内核原理到生产级全链路监控实战
分布式·后端·skywalking
橘子师兄6 分钟前
C++AI大模型接入SDK—Genimi接入封装
c++·人工智能·后端
进阶小白猿6 分钟前
Java技术八股学习Day26
java·开发语言·学习
余瑜鱼鱼鱼8 分钟前
synchronized总结
java·开发语言
小宇的天下9 分钟前
Calibre :SVRF rule file example
java·开发语言·数据库
码农水水11 分钟前
大疆Java面试被问:使用Async-profiler进行CPU热点分析和火焰图解读
java·开发语言·jvm·数据结构·后端·面试·职场和发展
我真的是大笨蛋17 分钟前
MVCC解析
java·数据库·spring boot·sql·mysql·设计模式·设计规范
秃头续命码农人19 分钟前
谈谈对Spring、Spring MVC、SpringBoot、SpringCloud,Mybatis框架的理解
java·spring boot·spring·mvc·maven·mybatis
ahauedu22 分钟前
SpringBoot 3.5.10引入springdoc-openapi-starter-webmvc-ui版本
java·spring boot·后端
我是咸鱼不闲呀23 分钟前
力扣Hot100系列15(Java)——[二叉树]总结(有效的括号,最小栈,字符串解码,每日温度,柱状图中最大的矩形)
java·算法·leetcode