Springboot解決跨域問(wèn)題的實(shí)現(xiàn)方案
Springboot如何解決跨域問(wèn)題?
在前后端分離架構(gòu)中,跨域問(wèn)題是Java開(kāi)發(fā)者繞不開(kāi)的“基礎(chǔ)門檻”。本文從跨域的本質(zhì)(同源策略)出發(fā),系統(tǒng)講解SpringBoot中5種主流跨域方案,每個(gè)方案包含原理解析+可運(yùn)行代碼+適用場(chǎng)景,幫你徹底解決跨域問(wèn)題。
一、先搞懂:為什么會(huì)有跨域?
跨域的根源是瀏覽器的同源策略(Same-Origin Policy)——這是瀏覽器的安全機(jī)制,要求請(qǐng)求的“源”必須與當(dāng)前頁(yè)面的“源”完全一致,才允許交互。也就是說(shuō)在兩個(gè)后端之間互相調(diào)用接口是沒(méi)有跨域問(wèn)題的。
1.1 同源的定義
“源”由三部分組成,三者完全相同才叫“同源”:
- 協(xié)議(如
http/https) - 域名(如
localhost/www.xxx.com) - 端口(如
8080/8090)
示例:當(dāng)前頁(yè)面是http://localhost:8080,以下請(qǐng)求會(huì)被判定為跨域:
https://localhost:8080(協(xié)議不同)http://127.0.0.1:8080(域名不同)http://localhost:8090(端口不同)
1.2 跨域的表現(xiàn)
瀏覽器會(huì)攔截跨域請(qǐng)求,控制臺(tái)拋出類似錯(cuò)誤:
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是早期跨域方案,前端后端需要同時(shí)更改,需要增加一個(gè)callback請(qǐng)求參數(shù),當(dāng)然這個(gè)callback參數(shù)也可以自定義。
2.1 原理
- 前端通過(guò)
<script>標(biāo)簽請(qǐng)求后端接口,傳遞回調(diào)函數(shù)名(如callback=handleData); - 后端將數(shù)據(jù)包裹在回調(diào)函數(shù)中返回(如
handleData({"msg":"success"})); - 前端提前定義回調(diào)函數(shù),接收并處理數(shù)據(jù)。
2.2 實(shí)戰(zhàn)代碼
后端接口
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參數(shù),返回包裹后的JSON
*/
@GetMapping("/info")
public String jsonpInfo(@RequestParam String callback) {
// 模擬返回的數(shù)據(jù)(比如根據(jù)id=16查詢到的信息)
String data = "{\"code\":200,\"msg\":\"success\",\"data\":{\"id\":16,\"name\":\"測(cè)試數(shù)據(jù)\",\"desc\":\"JSONP跨域示例\"}}";
// 拼接JSONP格式的響應(yīng)(回調(diào)函數(shù)名 + 數(shù)據(jù))
return callback + "(" + data + ")";
}
}
前端測(cè)試
<!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">發(fā)起JSONP請(qǐng)求</button>
<script>
$("#submitBtn").click(function () {
$.ajax({
url: 'http://localhost:8080/info?id=16', // 后端接口地址(帶參數(shù)id=16)
type: 'GET', // JSONP僅支持GET請(qǐng)求
dataType: 'jsonp', // 關(guān)鍵:指定數(shù)據(jù)類型為jsonp
jsonp: 'callback', // 告訴后端:前端用callback參數(shù)傳遞回調(diào)函數(shù)名(默認(rèn)就是callback,可省略)
success: function (response) {
// 成功接收后端返回的數(shù)據(jù)
console.log("JSONP請(qǐng)求成功,返回?cái)?shù)據(jù):", response);
alert("請(qǐng)求成功!數(shù)據(jù)ID:" + response.data.id + ",名稱:" + response.data.name);
},
error: function (err) {
console.error("JSONP請(qǐng)求失?。?, err);
alert("請(qǐng)求失敗,請(qǐng)檢查控制臺(tái)");
}
});
});
</script>
</body>
</html>
2.3 適用場(chǎng)景
僅適用于簡(jiǎn)單GET請(qǐng)求,或需要兼容老舊瀏覽器(如IE)的場(chǎng)景,不推薦現(xiàn)代項(xiàng)目使用。
三、方案2:全局配置(WebMvcConfigurer)
這是SpringBoot中最常用、最優(yōu)雅的跨域方案,通過(guò)配置全局CORS規(guī)則實(shí)現(xiàn)。
3.1 原理
基于W3C的CORS標(biāo)準(zhǔn),服務(wù)端通過(guò)響應(yīng)頭告知瀏覽器“允許哪些源的請(qǐng)求”,核心響應(yīng)頭包括:
Access-Control-Allow-Origin:允許的跨域源;Access-Control-Allow-Methods:允許的請(qǐng)求方法;Access-Control-Allow-Credentials:是否允許攜帶Cookie。
3.2 實(shí)戰(zhàn)代碼
全局配置類
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("*") // 允許所有源(生產(chǎn)環(huán)境建議指定域名)
.allowedMethods("GET", "POST", "PUT", "DELETE") // 允許的請(qǐng)求方法
.allowedHeaders("*") // 允許所有請(qǐng)求頭
.allowCredentials(true) // 允許攜帶Cookie
.maxAge(3600); // 預(yù)檢請(qǐng)求緩存1小時(shí)
}
}
測(cè)試接口
@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;
}
}
前端測(cè)試(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))">測(cè)試GET</button>
<button onclick="axios.post('http://localhost:8080/api/test/post', {name:'張三'}).then(res=>alert(res.data))">測(cè)試POST</button>
3.3 適用場(chǎng)景
前后端分離的現(xiàn)代項(xiàng)目,推薦作為默認(rèn)方案使用。
四、方案3:CorsFilter(官方Filter方案)
通過(guò)Spring官方的CorsFilter實(shí)現(xiàn)跨域,比手動(dòng)寫(xiě)Filter更規(guī)范、更靈活。
4.1 原理
CorsFilter是Spring封裝的CORS過(guò)濾器,通過(guò)CorsConfiguration配置規(guī)則,再綁定到指定路徑,自動(dòng)攔截請(qǐng)求并添加跨域響應(yīng)頭。
4.2 實(shí)戰(zhàn)代碼
配置類
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. 配置跨域規(guī)則
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); // 所有路徑應(yīng)用規(guī)則
// 3. 返回CorsFilter
return new CorsFilter(source);
}
}
4.3 適用場(chǎng)景
需要更靈活的跨域邏輯(如動(dòng)態(tài)調(diào)整跨域規(guī)則),或項(xiàng)目中已使用Filter鏈的場(chǎng)景。
五、方案4:局部配置(@CrossOrigin注解)
僅為個(gè)別接口/Controller配置跨域,優(yōu)先級(jí)高于全局配置。
5.1 原理
@CrossOrigin是Spring提供的注解,底層基于CORS機(jī)制,為標(biāo)注的接口自動(dòng)添加跨域響應(yīng)頭。
5.2 實(shí)戰(zhàn)代碼
單個(gè)接口配置
@RestController
public class CrossOriginController {
@CrossOrigin(origins = "*", maxAge = 3600)
@GetMapping("/api/cross/test")
public String crossTest() {
return "單個(gè)接口跨域成功";
}
}
Controller級(jí)配置
@CrossOrigin(origins = "http://localhost:8080") // 僅允許8080源
@RestController
public class CrossOriginController2 {
@GetMapping("/api/cross/test2")
public String crossTest2() {
return "Controller級(jí)跨域成功";
}
}
5.3 適用場(chǎng)景
個(gè)別接口需要單獨(dú)配置跨域的場(chǎng)景,不推薦全局使用。
六、方案對(duì)比與選型建議
| 方案 | 適用場(chǎng)景 | 優(yōu)點(diǎn) | 缺點(diǎn) |
|---|---|---|---|
| JSONP | 簡(jiǎn)單GET請(qǐng)求、兼容老舊瀏覽器 | 實(shí)現(xiàn)簡(jiǎn)單 | 僅支持GET、有安全風(fēng)險(xiǎn) |
| WebMvcConfigurer | 前后端分離全局跨域 | 優(yōu)雅簡(jiǎn)潔、易維護(hù) | 靈活性稍弱 |
| CorsFilter | 復(fù)雜跨域規(guī)則、Filter鏈場(chǎng)景 | 靈活可控、官方規(guī)范 | 代碼量略多 |
| @CrossOrigin | 個(gè)別接口/Controller跨域 | 局部配置、無(wú)需全局修改 | 不適合全局場(chǎng)景 |
到此這篇關(guān)于Springboot解決跨域問(wèn)題的實(shí)現(xiàn)方案的文章就介紹到這了,更多相關(guān)Springboot跨域內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- SpringBoot解決跨域的五種方式
- SpringBoot+Vue解決跨域問(wèn)題幾種處理方案介紹
- SpringBoot使用CORS解決無(wú)法跨域訪問(wèn)問(wèn)題的具體步驟
- springboot+vue項(xiàng)目怎么解決跨域問(wèn)題詳解
- 6種SpringBoot解決跨域請(qǐng)求的方法整理
- SpringBoot項(xiàng)目中如何解決跨域問(wèn)題的最新方案?
- SpringBoot+Vue跨域配置(CORS)問(wèn)題得解決過(guò)程
- SpringBoot中解決跨域的多種實(shí)現(xiàn)方式
- Springboot解決跨域問(wèn)題方案總結(jié)(包括Nginx,Gateway網(wǎng)關(guān)等)
相關(guān)文章
Java中四種9*9乘法表的實(shí)現(xiàn)方式(附代碼)
這篇文章主要介紹了Java中四種9*9乘法表的實(shí)現(xiàn)方式(附代碼),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-11-11
Spring?中?PageHelper?不生效問(wèn)題及解決方法
這篇文章主要介紹了Spring?中?PageHelper?不生效問(wèn)題,使用這個(gè)插件時(shí)要注意版本的問(wèn)題,不同的版本可能 PageHelper 不會(huì)生效,本文結(jié)合示例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下2022-12-12
mybatis實(shí)現(xiàn)獲取入?yún)⑹荓ist和Map的取值
這篇文章主要介紹了mybatis實(shí)現(xiàn)獲取入?yún)⑹荓ist和Map的取值問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-06-06
MyBatis動(dòng)態(tài)<if>標(biāo)簽使用避坑指南
這篇文章主要為大家介紹了MyBatis動(dòng)態(tài)<if>標(biāo)簽使用避坑指南,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-03-03
Java中如何將json字符串轉(zhuǎn)換成map/list
這篇文章主要介紹了Java中如何將json字符串轉(zhuǎn)換成map/list,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-07-07

