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 原理
- 前端通过
<script>标签请求后端接口,传递回调函数名(如callback=handledata); - 后端将数据包裹在回调函数中返回(如
handledata({"msg":"success"})); - 前端提前定义回调函数,接收并处理数据。
2.2 实战代码
后端接口
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 + ")";
}
}
前端测试
<!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 实战代码
全局配置类
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小时
}
}
测试接口
@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)
<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 实战代码
配置类
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 实战代码
单个接口配置
@restcontroller
public class crossorigincontroller {
@crossorigin(origins = "*", maxage = 3600)
@getmapping("/api/cross/test")
public string crosstest() {
return "单个接口跨域成功";
}
}
controller级配置
@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跨域 | 局部配置、无需全局修改 | 不适合全局场景 |
到此这篇关于springboot解决跨域问题的实现方案的文章就介绍到这了,更多相关springboot跨域内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论