關于SpringBoot中的跨域問題
一、同源策略
同源策略是由Netscape提出的一個著名的安全策略,它是瀏覽器最核心也最基本的安全功能,現(xiàn)在所有支持JavaScript的瀏覽器都會使用這個策略。
所謂同源是指 協(xié)議 、 域名 以及 端口 要相同。
同源策略是基于安全方面的考慮提出來的,這個策略本身沒問題,但是我們在實際開發(fā)中,由于各種原因又經(jīng)常有跨域的需求,傳統(tǒng)的跨域方案是JSONP,JSONP雖然能解決跨域但是有一個很大的局限性,那就是只支持GET請求,不支持其他類型的請求
而今天我們說的CORS(跨域源資源共享)(CORS,Cross-origin resource sharing)是一個W3C標準,它是一份瀏覽器技術的規(guī)范,提供了Web服務從不同網(wǎng)域傳來沙盒腳本的方法,以避開瀏覽器的同源策略,這是JSONP模式的現(xiàn)代版。
在Spring框架中,對于CORS也提供了相應的解決方案。
非同源限制
- 無法讀取非同源網(wǎng)頁的 Cookie、LocalStorage 和 IndexedDB
- 無法接觸非同源網(wǎng)頁的 DOM
- 無法向非同源地址發(fā)送 AJAX 請求
二、java后端實現(xiàn)CORS跨域請求的方式
- 返回新的CorsFilter
- 重寫 WebMvcConfigurer
- 使用注解 @CrossOrigin
- 手動設置響應頭 (HttpServletResponse)
- 自定web filter 實現(xiàn)跨域
注意:
- CorFilter / WebMvConfigurer / @CrossOrigin 需要 SpringMVC 4.2以上版本才支持,對應springBoot 1.3版本以上
- 上面前兩種方式屬于全局 CORS 配置,后兩種屬于局部 CORS配置。如果使用了局部跨域是會覆蓋全局跨域的規(guī)則,所以可以通過 @CrossOrigin 注解來進行細粒度更高的跨域資源控制。
- 其實無論哪種方案,最終目的都是修改響應頭,向響應頭中添加瀏覽器所要求的數(shù)據(jù),進而實現(xiàn)跨域
1.返回新的 CorsFilter(全局跨域)
在任意配置類,返回一個 新的 CorsFIlter Bean ,并添加映射路徑和具體的CORS配置路徑。
@Configuration
public class GlobalCorsConfig {
@Bean
public CorsFilter corsFilter() {
//1. 添加 CORS配置信息
CorsConfiguration config = new CorsConfiguration();
//放行哪些原始域
config.addAllowedOrigin("*");
//是否發(fā)送 Cookie
config.setAllowCredentials(true);
//放行哪些請求方式
config.addAllowedMethod("*");
//放行哪些原始請求頭部信息
config.addAllowedHeader("*");
//暴露哪些頭部信息
config.addExposedHeader("*");
//2. 添加映射路徑
UrlBasedCorsConfigurationSource corsConfigurationSource = new UrlBasedCorsConfigurationSource();
corsConfigurationSource.registerCorsConfiguration("/**",config);
//3. 返回新的CorsFilter
return new CorsFilter(corsConfigurationSource);
}
}2. 重寫 WebMvcConfigurer(全局跨域)
@Configuration
public class CorsConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
//是否發(fā)送Cookie
.allowCredentials(true)
//放行哪些原始域
.allowedOrigins("*")
.allowedMethods(new String[]{"GET", "POST", "PUT", "DELETE"})
.allowedHeaders("*")
.exposedHeaders("*");
}
}3. 使用注解 (局部跨域)
在控制器(類上)上使用注解 @CrossOrigin :,表示該類的所有方法允許跨域。
@RestController
@CrossOrigin(origins = "*")
public class HelloController {
@RequestMapping("/hello")
public String hello() {
return "hello world";
}
}在方法上使用注解 @CrossOrigin:
@RequestMapping("/hello")
@CrossOrigin(origins = "*")
//@CrossOrigin(value = "http://localhost:8081") //指定具體ip允許跨域
public String hello() {
return "hello world";
}4. 手動設置響應頭(局部跨域)
使用 HttpServletResponse 對象添加響應頭(Access-Control-Allow-Origin)來授權(quán)原始域,這里 Origin的值也可以設置為 “*”,表示全部放行。
@RequestMapping("/index")
public String index(HttpServletResponse response) {
response.addHeader("Access-Allow-Control-Origin","*");
return "index";
}5. 使用自定義filter實現(xiàn)跨域
首先編寫一個過濾器,可以起名字為MyCorsFilter.java
@Component
public class MyCorsFilter implements Filter {
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletResponse response = (HttpServletResponse) res;
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers", "x-requested-with,content-type");
chain.doFilter(req, res);
}
public void init(FilterConfig filterConfig) {}
public void destroy() {}
}在web.xml中配置這個過濾器,使其生效
<!-- 跨域訪問 START--> <filter> <filter-name>CorsFilter</filter-name> <filter-class>com.mesnac.aop.MyCorsFilter</filter-class> </filter> <filter-mapping> <filter-name>CorsFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <!-- 跨域訪問 END -->
三、例子
首先創(chuàng)建兩個普通的SpringBoot項目,第一個命名為provider提供服務,第二個命名為consumer消費服務,第一個配置端口為8080,第二個配置配置為8081,然后在provider上提供兩個hello接口,一個get,一個post,如下:
@RestController
public class Provider {
@GetMapping("/hello")
public String hello() {
return "hello";
}
@PostMapping("/hello")
public String hello2() {
return "post hello";
}
}在consumer的resources/templates目錄下創(chuàng)建一個html文件,發(fā)送一個簡單的ajax請求,如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>CORS</title>
</head>
<body>
<div id="app"></div>
<input type="button" onclick="btnClick()" value="get_button">
<input type="button" onclick="btnClick2()" value="post_button">
<script>
function btnClick() {
$.get('http://localhost:8080/hello', function (msg) {
$("#app").html(msg);
});
}
function btnClick2() {
$.post('http://localhost:8080/hello', function (msg) {
$("#app").html(msg);
});
}
</script>
</body>
</html>然后分別啟動兩個項目,發(fā)送請求按鈕,觀察瀏覽器控制臺如下:
Access to XMLHttpRequest at ‘//localhost:8080/hello’ from origin ‘//localhost:8081’ has been blocked by CORS policy: No ‘Access-Control-Allow-Origin’ header is present on the requested resource
可以看到,由于同源策略的限制,請求無法發(fā)送成功。
使用CORS可以在前端代碼不做任何修改的情況下,實現(xiàn)跨域,那么接下來看看在provider中如何配置。首先可以通過@CrossOrigin注解配置某一個方法接受某一個域的請求,如下:
@RestController
public class Provider {
@CrossOrigin(value = "http://localhost:8081")
@GetMapping("/hello")
public String hello() {
return "hello";
}
@CrossOrigin(value = "http://localhost:8081")
@PostMapping("/hello")
public String hello2() {
return "post hello";
}
}這個注解表示這兩個接口接受來自//localhost:8081地址的請求,配置完成后,重啟provider,再次發(fā)送請求,瀏覽器控制臺就不會報錯了,consumer也能拿到數(shù)據(jù)了。
provider上,每一個方法上都去加注解未免太麻煩了,在Spring Boot中,還可以通過全局配置一次性解決這個問題,全局配置只需要在配置類中重寫addCorsMappings方法即可,如下:
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("http://localhost:8081")
.allowedMethods("*")
.allowedHeaders("*");
}
}/**表示本應用的所有方法都會去處理跨域請求,allowedMethods表示允許通過的請求數(shù),allowedHeaders則表示允許的請求頭。經(jīng)過這樣的配置之后,就不必在每個方法上單獨配置跨域了。
四、存在的問題
了解了整個CORS的工作過程之后,我們通過Ajax發(fā)送跨域請求,雖然用戶體驗提高了,但是也有潛在的威脅存在,常見的就是CSRF(Cross-site request forgery)跨站請求偽造??缯菊埱髠卧煲脖环Q為one-click attack 或者 session riding,通常縮寫為CSRF或者XSRF,是一種挾制用戶在當前已登錄的Web應用程序上執(zhí)行非本意的操作的攻擊方法,舉個例子:
假如一家銀行用以運行轉(zhuǎn)賬操作的URL地址如下://icbc.com/aa?bb=cc,那么,一個惡意攻擊者可以在另一個網(wǎng)站上放置如下代碼:<img src="//icbc.com/aa?bb=cc">,如果用戶訪問了惡意站點,而她之前剛訪問過銀行不久,登錄信息尚未過期,那么她就會遭受損失。
基于此,瀏覽器在實際操作中,會對請求進行分類,分為簡單請求,預先請求,帶憑證的請求等,預先請求會首先發(fā)送一個options探測請求,和瀏覽器進行協(xié)商是否接受請求。默認情況下跨域請求是不需要憑證的,但是服務端可以配置要求客戶端提供憑證,這樣就可以有效避免csrf攻擊。
到此這篇關于關于SpringBoot中的跨域問題的文章就介紹到這了,更多相關SpringBoot 跨域內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
springboot3.0整合mybatis-flex實現(xiàn)逆向工程的示例代碼
逆向工程先創(chuàng)建數(shù)據(jù)庫表,由框架負責根據(jù)數(shù)據(jù)庫表,自動生成mybatis所要執(zhí)行的代碼,本文就來介紹一下springboot mybatis-flex逆向工程,感興趣的可以了解一下2024-06-06
Mybatis關聯(lián)查詢結(jié)果集對象嵌套的具體使用
在查詢時經(jīng)常出現(xiàn)一對多”的關系,所有會出現(xiàn)嵌套對象的情況,本文主要介紹了Mybatis關聯(lián)查詢結(jié)果集對象嵌套的具體使用,文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2022-02-02

