SpringBoot 中到底如何解決跨域問(wèn)題?
大家好,我是路人,這是SpringMVC系列第31篇。
今天又給大家?guī)?lái)了一個(gè)很重要的知識(shí)點(diǎn):SpringMVC中如何處理跨域問(wèn)題,本文的內(nèi)容同樣適合于SpringBoot
1、跨域訪(fǎng)問(wèn)報(bào)錯(cuò)
當(dāng)一個(gè)請(qǐng)求url的協(xié)議、域名、端口三者之間任意一個(gè)與當(dāng)前頁(yè)面url不同即為跨域。
出于安全原因,瀏覽器禁止Ajax調(diào)用駐留在當(dāng)前原點(diǎn)之外的資源,比如從a.com發(fā)送一個(gè)ajax請(qǐng)求到b.com,則瀏覽器控制臺(tái)會(huì)報(bào)跨域訪(fǎng)問(wèn)錯(cuò)誤。
如下圖,從http://localhost:63342/站點(diǎn)頁(yè)面中向ttp://localhost:8080/chat21/cors/test2發(fā)送一個(gè)ajax請(qǐng)求,則出現(xiàn)了紅色的錯(cuò)誤信息,錯(cuò)誤中包含了Access-Controll-Allow-Origin這樣字樣的錯(cuò)誤,以后看到這個(gè)的時(shí)候,大家就要一眼看出來(lái)這是跨域問(wèn)題。

2、同源定義
同源策略是瀏覽器的一個(gè)重要的安全策略,它用于限制一個(gè)源的文檔或其加載的腳本如何與另外一個(gè)源進(jìn)行交互,它能夠隔絕惡意文檔,減少被攻擊的媒介。
如果兩個(gè)URL的協(xié)議、主機(jī)名和端口號(hào)都是相同的,那么這兩個(gè)URL就是同源的,否則不同源,不同源的訪(fǎng)問(wèn)就會(huì)出現(xiàn)跨域問(wèn)題,就會(huì)出現(xiàn)上面的錯(cuò)誤。
下表給出了與 URL http://store.company.com/dir/page.html 的源進(jìn)行對(duì)比的示例:
| URL | 結(jié)果 | 原因 |
|---|---|---|
| http://store.company.com/dir2/other.html | 同源 | 只有路徑不同 |
| https://store.company.com/secure.html | 非同源 | 協(xié)議不同 |
| http://store.company.com:81/dir/etc.html | 非同源 | 端口號(hào)不同 |
| http://news.company.com/dir/other.html | 非同源 | 主機(jī)名不同 |
也就是說(shuō)當(dāng)在http://store.company.com/dir/page.html這個(gè)網(wǎng)站中向https://store.company.com、http://store.company.com:81和http://news.company.com三個(gè)地址發(fā)起AXJX請(qǐng)求都會(huì)失敗并且會(huì)報(bào)跨域的錯(cuò)誤。這就是瀏覽器的同源策略,只能訪(fǎng)問(wèn)同源的數(shù)據(jù)。
3、跨域問(wèn)題如何解決?
跨域問(wèn)題需要使用CORS來(lái)解決,請(qǐng)求端和后端接口需要遵循CORS規(guī)則來(lái)通信,便可解決跨域訪(fǎng)問(wèn)的問(wèn)題。
CORS全稱(chēng)Cross-Origin Resource Sharing, 即跨域資源共享,是一個(gè)由一系列HTTP頭組成的系統(tǒng),這些HTTP頭決定瀏覽器是否阻止前端javascript代碼獲取跨域請(qǐng)求的響應(yīng)。為什么需要CORS ?這是因?yàn)闉g覽器存在同源安全策略,當(dāng)我們?cè)诋?dāng)前域請(qǐng)求另外一個(gè)域的資源時(shí),瀏覽器默認(rèn)會(huì)阻止腳本讀取它的響應(yīng),這時(shí)CORS就有了用武之地。
跨源資源共享(CORS)是由大多數(shù)瀏覽器實(shí)現(xiàn)的W3C規(guī)范,允許您靈活地指定什么樣的跨域請(qǐng)求被授權(quán),而不是使用一些不太安全和不太強(qiáng)大的策略,如IFRAME或JSONP等。
4、CORS原理
CORS的原理:簡(jiǎn)單點(diǎn)說(shuō),就是在請(qǐng)求頭或響應(yīng)頭中添加了一些配置,通過(guò)這些配置來(lái)便可輕松解決跨域問(wèn)題。
想詳細(xì)了解CORS原理的,建議先閱讀下面2篇文章,然后再繼續(xù)向下看,否則,最后你知道SpringMVC是如何解決的,但是不知道本質(zhì)的原理是什么。
CORS通信:http://itsoku.com/article/197
瀏覽器安全策略 & CORS:http://itsoku.com/article/198
5、SpringMVC中如何解決跨域問(wèn)題?
SpringMVC內(nèi)部提供了跨域問(wèn)題的解決方案,只需要做一些簡(jiǎn)單的配置,而接口基本上不用做任何修改,便可解決跨域問(wèn)題。
SpringMVC解決跨域問(wèn)題的原理也就是SpringMVC遵循了CORS通信的規(guī)則來(lái)解決了跨域的問(wèn)題,在響應(yīng)頭中添加了一些CORS需要的信息。
SpringMVC中提供了3種方案來(lái)解決跨域問(wèn)題,下面一起來(lái)了解下。
6、方案1:方法或者類(lèi)上標(biāo)注@CrossOrigin注解
接口方法上標(biāo)注 org.springframework.web.bind.annotation.CrossOrigin注解,如下test1接口上標(biāo)注了@CrossOrigin注解,這個(gè)接口就支持跨域訪(fǎng)問(wèn),@CrossOrigin注解中含有更詳細(xì)的配置,這里就不細(xì)說(shuō)了也可以在類(lèi)上標(biāo)注 @CrossOrigin注解,那么這個(gè)類(lèi)中所有接口會(huì)支持跨域訪(fǎng)問(wèn)也可同時(shí)在類(lèi)和方法上標(biāo)注 @CrossOrigin注解,最后方法上的跨域訪(fǎng)問(wèn)會(huì)取合并后的配置
@RestController
public?class?CorsController?{
????@RequestMapping("/cors/test1")
????@CrossOrigin
????public?List?test1()? {
????????List?result?=?Arrays.asList("www.itsoku.com",
????????????????"Spring高手系列",
????????????????"SpringMVC系列",
????????????????"MySQL系列",
????????????????"高并發(fā)系列");
????????return?result;
????}
}
7、方案2:全局配置的方式
除了細(xì)粒度、基于注釋的配置之外,您還可能需要定義一些全局CORS配置,這類(lèi)似于使用篩選器,但可以聲明為Spring MVC并結(jié)合細(xì)粒度@CrossOrigin配置。默認(rèn)情況下,所有origins and GET, HEAD and POST methods是允許的。
@EnableWebMvc
@Configuration
public?class?MvcConfig?implements?WebMvcConfigurer?{
????@Override
????public?void?addCorsMappings(CorsRegistry?registry)?{
????????//每次調(diào)用registry.addMappin可以添加一個(gè)跨域配置,需要多個(gè)配置可以多次調(diào)用registry.addMapping
????????registry.addMapping("/**")
????????????????.allowedOrigins("*")?//放行哪些原始域
????????????????.allowedMethods("PUT",?"DELETE","POST",?"GET")?//放行哪些請(qǐng)求方式
????????????????.allowedHeaders("header1",?"header2",?"header3")?//放行哪些原始請(qǐng)求頭部信息
????????????????.exposedHeaders("header1",?"header2")?//暴露哪些頭部信息
????????????????.allowCredentials(false)?//是否發(fā)送?Cookie
????????????????.maxAge(3600);
????????//?Add?more?mappings...
????}
}
8、方案3:攔截器的方式CorsFilter
//處理跨域的Filter
//1.?添加?CORS配置信息
CorsConfiguration?config?=?new?CorsConfiguration();
//放行哪些原始域
config.addAllowedOrigin("*");
//是否發(fā)送?Cookie
config.setAllowCredentials(false);
//放行哪些請(qǐng)求方式
config.addAllowedMethod("*");
//放行哪些原始請(qǐng)求頭部信息
config.addAllowedHeader("*");
//暴露哪些頭部信息
config.addExposedHeader("*");
//2.?添加映射路徑
UrlBasedCorsConfigurationSource?corsConfigurationSource?=?new?UrlBasedCorsConfigurationSource();
corsConfigurationSource.registerCorsConfiguration("/**",config);
9、案例代碼
9.1、案例完整代碼
git地址:https://gitee.com/javacode2018/springmvc-series

9.2、接口代碼:CorsController
CorsController中有2個(gè)接口,第一個(gè)接口上標(biāo)注了@CrossOrigin注解,可以解決跨域訪(fǎng)問(wèn)的問(wèn)題,而第二個(gè)方法沒(méi)有標(biāo)注。
@RestController
public?class?CorsController?{
????@RequestMapping("/cors/test1")
????@CrossOrigin
????public?List?test1()? {
????????List?result?=?Arrays.asList("www.itsoku.com",
????????????????"Spring高手系列",
????????????????"SpringMVC系列",
????????????????"MySQL系列",
????????????????"高并發(fā)系列");
????????return?result;
????}
????@RequestMapping("/cors/test2")
????public?List?test2()? {
????????List?result?=?Arrays.asList("www.itsoku.com",
????????????????"Spring高手系列",
????????????????"SpringMVC系列",
????????????????"MySQL系列",
????????????????"高并發(fā)系列");
????????return?result;
????}
}
9.3、靜態(tài)頁(yè)面:cors.html
靜態(tài)頁(yè)面cors.html中添加了2個(gè)按鈕,點(diǎn)擊2個(gè)按鈕的時(shí)候,分別以ajax跨域的方式訪(fǎng)問(wèn)上面2個(gè)接口,第1個(gè)按鈕訪(fǎng)問(wèn)第一個(gè)接口,第2個(gè)按鈕訪(fǎng)問(wèn)第二個(gè)接口,然后在瀏覽器控制臺(tái)查看效果。
html>
<html?lang="en">
<head>
????<meta?charset="UTF-8">
????<title>corstitle>
????<script?type="text/javascript"?src="jquery-3.6.0.min.js">script>
????<script?type="text/javascript">
????????$(function?()?{
????????????$("#cors-btn1").click(function?()?{
????????????????$.ajax({
????????????????????url:?"http://localhost:8080/chat21/cors/test1",
????????????????????success:?function?(data)?{
????????????????????????console.log(JSON.stringify(data));
????????????????????}
????????????????});
????????????});
????????????$("#cors-btn2").click(function?()?{
????????????????$.ajax({
????????????????????url:?"http://localhost:8080/chat21/cors/test2",
????????????????????success:?function?(data)?{
????????????????????????console.log(JSON.stringify(data));
????????????????????}
????????????????});
????????????});
????????})
????script>
head>
<body>
<button?id="cors-btn1">跨域測(cè)試test1button>
<button?id="cors-btn2">跨域測(cè)試test2button>
body>
html>
9.4、將chat21-cores模塊發(fā)布到tomcat


9.5、運(yùn)行靜態(tài)頁(yè)面cors.html
在idea中選中cors.html,然后鼠標(biāo)右鍵->Run,即可運(yùn)行

運(yùn)行效果如下(最好以chrome瀏覽器運(yùn)行),idea中支持直接運(yùn)行靜態(tài)頁(yè)面,大家注意這里的端口是63342,而上面tomcat的端口是8080,然后瀏覽器中按F12打開(kāi)瀏覽器控制臺(tái),選中Console選項(xiàng)卡,稍后在這里可以看到點(diǎn)擊按鈕驗(yàn)證跨域的效果。

9.6、點(diǎn)擊第1個(gè)按鈕,測(cè)試跨域正常請(qǐng)求

再看看下面這個(gè)圖,正常的跨域請(qǐng)求,響應(yīng)頭多了幾個(gè)頭,主要是Access-Control開(kāi)頭的頭是和CORS相關(guān)的,瀏覽器就是根據(jù)這些響應(yīng)頭來(lái)決定跨域訪(fǎng)問(wèn)是不是正常的,如果沒(méi)有這些頭,瀏覽器將拒絕讀取響應(yīng)體,然后就報(bào)錯(cuò)啦。

9.7、點(diǎn)擊第2個(gè)按鈕,測(cè)試跨域異常請(qǐng)求


10、總結(jié)
掌握SpringMVC中解決跨域問(wèn)題的3種方式
注解的方式:@CrossOrigin 全局配置的方式:WebMvcConfigurer接口的addCorsMappings方法中注冊(cè)CORS配置 攔截器的方式:CorsFilter
11、SpringMVC系列目錄
SpringMVC系列第1篇:helloword SpringMVC系列第2篇:@Controller、@RequestMapping SpringMVC系列第3篇:異常高效的一款接口測(cè)試?yán)?/a> SpringMVC系列第4篇:controller常見(jiàn)的接收參數(shù)的方式 SpringMVC系列第5篇:@RequestBody大解密,說(shuō)點(diǎn)你不知道的 SpringMVC系列第6篇:上傳文件的 4 種方式,你都會(huì)么? SpringMVC系列第7篇:SpringMVC返回視圖常見(jiàn)的 5 種方式,你會(huì)幾種? SpringMVC系列第8篇:返回json & 通用返回值設(shè)計(jì) SpringMVC系列第9篇:SpringMVC返回null是什么意思? SpringMVC系列第10篇:異步處理 SpringMVC系列第11篇:集成靜態(tài)資源 SpringMVC系列第12篇:攔截器 SpringMVC系列第13篇:統(tǒng)一異常處理 SpringMVC系列第14篇:實(shí)戰(zhàn)篇:通用返回值 & 異常處理設(shè)計(jì) SpringMVC系列第15篇:全注解的方式?&?原理解析 SpringMVC系列第16篇:通過(guò)源碼解析SpringMVC處理請(qǐng)求的流程 SpringMVC系列第17篇:源碼解析SpringMVC容器的啟動(dòng)過(guò)程 SpringMVC系列第18篇:強(qiáng)大的RequestBodyAdvice解密 SpringMVC系列第19篇:強(qiáng)大的ResponseBodyAdvice解密 SpringMVC系列第20篇:RestFull詳解 SpringMVC系列第21篇:接口調(diào)用過(guò)利器RestTemplate SpringMVC系列第22篇:參數(shù)解析器HandlerMethodArgumentResolver解密 SpringMVC系列第23篇:@RequestParam用法及原理詳解 SpringMVC系列第24篇:@RequestBody用法及原理詳解 SpringMVC系列第25篇:@RequestHeader用法及原理詳解 SpringMVC系列第26篇:@CookieValue用法及原理詳解 SpringMVC系列第27篇:@RequestAttribute詳解 SpringMVC系列第28篇:@SessionAttribute詳解 SpringMVC系列第29篇:重定向和轉(zhuǎn)向詳解
12、更多系列文章
Spring高手系列(共56篇) Java高并發(fā)系列(共34篇) MySql高手系列(共27篇) Maven高手系列(共10篇) Mybatis系列(共12篇) 聊聊db和緩存一致性常見(jiàn)的實(shí)現(xiàn)方式 接口冪等性這么重要,它是什么?怎么實(shí)現(xiàn)? 泛型,有點(diǎn)難度,會(huì)讓很多人懵逼,那是因?yàn)槟銢](méi)有看這篇文章!
13、最新資料
尚硅谷 Java 學(xué)科全套教程(總 207.77GB) 2021 最新版 Java 微服務(wù)學(xué)習(xí)線(xiàn)路圖 + 視頻 阿里技術(shù)大佬整理的《Spring 學(xué)習(xí)筆記.pdf》 阿里大佬的《MySQL 學(xué)習(xí)筆記高清.pdf》 2021 版 java 高并發(fā)常見(jiàn)面試題匯總.pdf Idea 快捷鍵大全.pdf
