Java實(shí)現(xiàn)后端跨域的常見解決方案
一、搭建服務(wù)(cross-server)
1.1、maven依賴
pom.xml
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.16.14</version> </dependency> </dependencies>
1.2、接口
@Slf4j @RestController @RequestMapping("/cross") public class CrossController { @PostMapping("/request") public String crossRequest(HttpServletRequest request) { log.info("接收到跨域請求參數(shù):{}",request.getParameter("name")); return "Request CrossServer Success"; } }
1.3、配置
application.properties
server.port=8080 server.servlet.context-path=/crossServer
至此我們就提供了一個(gè)接口:http://localhost:8080/crossServer/cross/request
二、搭建服務(wù)(cross-web)
2.1、maven依賴
pom.xml
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.16.14</version> </dependency> </dependencies>
2.2、接口
@Controller @RequestMapping("/test") public class WebController { @RequestMapping(value = "/request") public String request(HttpServletRequest request) { return "/cross"; } }
2.3、頁面
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>跨域測試頁面</title> <script src="js/jquery.min.js"></script> </head> <body> <h1>跨域測試</h1> <div> <input type="button" onclick="mySubmit()" value=" 發(fā)送跨域請求 "> </div> <script> function mySubmit() { // 發(fā)送跨域請求 jQuery.ajax({ url: "http://localhost:8086/test", type: "POST", data: {"name": "Java"}, success: function (result) { alert("返回?cái)?shù)據(jù):" + result.data); } }); } </script> </body> </html>
2.4、配置
application.properties
server.port=8081 server.servlet.context-path=/crossWeb spring.freemarker.request-context-attribute=request spring.freemarker.charset=UTF-8 spring.freemarker.template-loader-path=classpath:/templates/ spring.freemarker.suffix=.ftl spring.freemarker.prefix=/views
至此我們就提供了一個(gè)接口:http://localhost:8081/crossWeb/test/request,訪問此頁面可以打開如下測試頁面:
2.5、跨域請求結(jié)果
在沒有任何跨域處理的情況下,我們點(diǎn)擊按鈕,發(fā)起請求,得到結(jié)果如下:
Access to XMLHttpRequest at 'http://localhost:8080/crossServer/cross/request' from origin 'http://localhost:8081' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. POST http://localhost:8080/crossServer/cross/request net::ERR_FAILED 200
2.6、常見跨域情況
請求地址 | 說明 | 是否跨域 |
---|---|---|
http://www.alian.com/a http://www.alian.com/b | 同域名 | 否 |
http://www.alian.com/a/b http://www.alian.com/c/d | 同域名不同文件夾 | 否 |
http://www.alian.com/a https://www.alian.com/a | 同域名不同協(xié)議 | 是 |
http://www.alian.com/a http://www.alian.com:8080/a | 同域名下不同端口 | 是 |
http://www.alian.com/a http://yun.alian.com/a | 同頂級域名不同二級域名 | 是 |
http://www.alian.com/a http://www.yang.com/a | 不同域名 | 是 |
http://www.alian.com/a http://10.130.1.88:80/a | 域名和對應(yīng)的ip | 是 |
三、解決方案
3.1、通過 @CrossOrigin 注解
最簡單的方式是通過使用注解@CrossOrigin,但是需要注意的是這個(gè)注解版本,這個(gè)注解是從Spring Framework 4.2 GA開始有的。如果你項(xiàng)目的版本低于這個(gè)版本是用不了的。
package com.alian.crossserver.controller; import lombok.extern.slf4j.Slf4j; import org.springframework.web.bind.annotation.CrossOrigin; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import javax.servlet.http.HttpServletRequest; @Slf4j @RestController @RequestMapping("/cross") public class CrossController { @CrossOrigin(origins = "*") @PostMapping("/request") public String crossRequest(HttpServletRequest request) { log.info("接收到跨域請求參數(shù):{}",request.getParameter("name")); return "Request CrossServer Success"; } }
3.2、通過配置類
為了避免方法間相互影響,我們先在我們的方法crossRequest,去除注解@CrossOrigin(origins = “*”),后面也是一樣就不一一提示了。
package com.alian.crossserver.config; 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 CorsConfig implements WebMvcConfigurer { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") // 所有接口 .allowCredentials(true) // 是否發(fā)送 Cookie .allowedOriginPatterns("*") // 支持域 .allowedMethods("GET", "POST", "PUT", "DELETE") // 支持方法 .allowedHeaders("*") .exposedHeaders("*"); } }
上面是對全部方法都支持跨域,我們也可以指定某個(gè)請求支持跨域,如下:
@Configuration public class CorsConfig implements WebMvcConfigurer { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/cross/request") // 指定接口 .allowCredentials(true) // 是否發(fā)送 Cookie .allowedOriginPatterns("*") // 支持域 .allowedMethods("GET", "POST", "PUT", "DELETE") // 支持方法 .allowedHeaders("*") .exposedHeaders("*"); } }
3.3、通過 CorsFilter 對象
再沒有使用以上方式解決跨域的情況下,我們使用過濾器也可以實(shí)現(xiàn)。
package com.alian.crossserver.filter; 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; @Configuration public class CustomCorsFilter { @Bean public CorsFilter corsFilter() { // 創(chuàng)建cors配置對象 CorsConfiguration config = new CorsConfiguration(); // 支持域 config.addAllowedOriginPattern("*"); // 是否發(fā)送 Cookie config.setAllowCredentials(true); // 支持請求方式 config.addAllowedMethod("*"); // 允許的原始請求頭部信息 config.addAllowedHeader("*"); // 暴露的頭部信息 config.addExposedHeader("*"); // 添加地址映射 UrlBasedCorsConfigurationSource corsConfigurationSource = new UrlBasedCorsConfigurationSource(); corsConfigurationSource.registerCorsConfiguration("/**", config); // 返回 CorsFilter 對象 return new CorsFilter(corsConfigurationSource); } }
上面是對所有域名請求都支持跨域,我們也可以指定某個(gè)域名,意思就是只有指定域名可以跨域請求。比如:我們只允許http://localhost:8081的跨域請求。
@Configuration public class CustomCorsFilter { @Bean public CorsFilter corsFilter() { // 創(chuàng)建cors配置對象 CorsConfiguration config = new CorsConfiguration(); // 支持指定域 config.addAllowedOriginPattern("http://localhost:8081"); // 是否發(fā)送 Cookie config.setAllowCredentials(true); // 支持請求方式 config.addAllowedMethod("*"); // 允許的原始請求頭部信息 config.addAllowedHeader("*"); // 暴露的頭部信息 config.addExposedHeader("*"); // 添加地址映射 UrlBasedCorsConfigurationSource corsConfigurationSource = new UrlBasedCorsConfigurationSource(); corsConfigurationSource.registerCorsConfiguration("/**", config); // 返回 CorsFilter 對象 return new CorsFilter(corsConfigurationSource); } }
3.4、通過 Response 對象
在沒有使用以上方式解決跨域的情況下,我們通過Response對象也可以實(shí)現(xiàn)跨域。
@Slf4j @RestController @RequestMapping("/cross") public class CrossController { @PostMapping("/request") public String crossRequest(HttpServletRequest request, HttpServletResponse response) { response.setHeader("Access-Control-Allow-Origin", "*"); log.info("接收到跨域請求參數(shù):{}",request.getParameter("name")); return "Request CrossServer Success"; } }
3.5、通過實(shí)現(xiàn) ResponseBodyAdvice 對象
如果是嫌上述方法在每個(gè)方法里都要設(shè)置response.setHeader(“Access-Control-Allow-Origin”, “*”),那就可以統(tǒng)一處理返回??梢岳聾ControllerAdvice和ResponseBodyAdvice通知完成。
package com.alian.crossserver.advice; import org.springframework.core.MethodParameter; import org.springframework.http.MediaType; import org.springframework.http.server.ServerHttpRequest; import org.springframework.http.server.ServerHttpResponse; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice; @ControllerAdvice // 織入通知 public class ResponseAdvice implements ResponseBodyAdvice { /** * 內(nèi)容是否需要重寫(返回true表示重寫) * * @param returnType * @param converterType * @return */ @Override public boolean supports(MethodParameter returnType, Class converterType) { return true; } /** * 放回結(jié)果前調(diào)用 * * @param body * @param returnType * @param selectedContentType * @param selectedConverterType * @param request * @param response * @return */ @Override public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) { // 設(shè)置跨域 response.getHeaders().set("Access-Control-Allow-Origin", "*"); return body; } }
- supports:判斷是否要交給 beforeBodyWrite 方法執(zhí)行,ture:需要;false:不需要
- beforeBodyWrite:對 response 進(jìn)行具體的處理
3.6、通過 jsonp 方式
可能還有些老系統(tǒng)spring版本是Spring Framework 4.2 GA之前的,比如3.X??赡苌厦嬗行┓绞揭灿貌涣?,比如注解。這里我們可以使用jsonp方式,這里我們先改下我們的前端和后端。
3.6.1、后端
在之前的控制層增加一個(gè)接口jsonpRequest
@Slf4j @RestController @RequestMapping("/cross") public class CrossController { @RequestMapping("/jsonpRequest") public String jsonpRequest(HttpServletRequest request, HttpServletResponse response,@RequestParam("callback") String callback) { log.info("1接收到跨域請求參數(shù):姓名:{},年齡:{}",request.getParameter("name"),request.getParameter("age")); log.info("2接收到跨域請求參數(shù):{}",callback); String json="{\"msg\":\"Request CrossServer Success\"}"; return callback+"("+ json +")"; } }
這里需要注意的兩個(gè)點(diǎn):
- 一個(gè)是請求的方法得支持GET方法(@RequestMapping就是POST、GET都支持)
- 返回的結(jié)果轉(zhuǎn)為json字符串,作為回調(diào)方法的參數(shù)
3.6.2、前端方法一
對應(yīng)前端第一種方法是jsonp約定俗成的默認(rèn)值為callback
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>test cross</title> <script type="text/javascript" src="https://code.jquery.com/jquery-3.0.0.min.js"></script> </head> <body> <h1>跨域接口測試</h1> <div> <input type="button" onclick="crossRequest()" value="發(fā)送跨域請求"> <input type="button" onclick="crossCallBack()" value="老版本發(fā)送跨域請求"> </div> <script> function crossRequest() { // 發(fā)送跨域請求 jQuery.ajax({ url: "http://localhost:8080/crossServer/cross/request", type: "POST", data: {"name": "alian"}, success: function (result) { alert("請求返回?cái)?shù)據(jù):" + result); } }); } // 只看這個(gè)方法 function crossCallBack() { // 發(fā)送跨域請求 jQuery.ajax({ url: "http://localhost:8080/crossServer/cross/jsonpRequest", type: "POST", data: {"name": "alian","age": "28"}, dataType: "jsonp", //指定服務(wù)器返回的數(shù)據(jù)類型 jsonp: "callback", success: function (data) { alert("success回調(diào):"+JSON.stringify(data)); } }); } </script> </body> </html>
- dataType:該參數(shù)必須要設(shè)置成jsonp
- jsonp:該參數(shù)的值需要與服務(wù)器端約定(默認(rèn)值為callback)
- 成功進(jìn)入到success
所以我的后端接口就有一個(gè)參數(shù)接收
@RequestParam("callback") String callback
3.6.3、前端方法二
假設(shè)你不想用默認(rèn)的方法,比如方法名占用了,怎么辦呢?那就像下面這樣自定義方法名:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>test cross</title> <script type="text/javascript" src="https://code.jquery.com/jquery-3.0.0.min.js"></script> </head> <body> <h1>跨域接口測試</h1> <div> <input type="button" onclick="crossRequest()" value="發(fā)送跨域請求"> <input type="button" onclick="crossCallBack()" value="老版本發(fā)送跨域請求"> </div> <script> function crossRequest() { // 發(fā)送跨域請求 jQuery.ajax({ url: "http://localhost:8080/crossServer/cross/request", type: "POST", data: {"name": "alian"}, success: function (result) { alert("請求返回?cái)?shù)據(jù):" + result); } }); } // 看這個(gè)方法 function crossCallBack() { // 發(fā)送跨域請求 jQuery.ajax({ url: "http://localhost:8080/crossServer/cross/jsonpRequest?callback=jsonpCallBack", type: "POST", data: {"name": "alian","age": "28"}, dataType: "jsonp", //指定服務(wù)器返回的數(shù)據(jù)類型 jsonp: "jsonpCallBack", success: function (data) { } }); } // 這個(gè)是回調(diào)方法了,不會進(jìn)入到success了 function jsonpCallBack(data){ var result = JSON.stringify(data); //json對象轉(zhuǎn)成字符串 alert("Jsonp請求返回?cái)?shù)據(jù):" + result); } </script> </body> </html>
- 首先你得指定你的jsonp方法,比如我這里是jsonpCallBack,也就是回調(diào)方法
- 同時(shí)我們請求里需要把回調(diào)方法名傳遞到后臺,比如我這里callback=jsonpCallBack
- 回調(diào)就會直接進(jìn)入到我們的jsonpCallBack方法
jsonpCallBack是自定義方法名,你也可以改成其他的名字,記得請求參數(shù),請求地址,及其方法定義,三個(gè)一起改哦。
兩者的結(jié)果如下:
總結(jié)
跨域是瀏覽器的行為,跨域問題其實(shí)就是瀏覽器的同源策略所導(dǎo)致的,所以后端就沒有跨域一說了。以上幾種跨域解決方法單獨(dú)用一個(gè)就行了,盡量不要混用,以免出現(xiàn)其他重復(fù)沖突的問題。還有其他一些通過nginx或者網(wǎng)關(guān)等方法都可以解決跨域問題,就不一一介紹了。
以上就是Java實(shí)現(xiàn)后端跨域的常見解決方案的詳細(xì)內(nèi)容,更多關(guān)于Java后端跨域的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
java中應(yīng)用Stack進(jìn)行算術(shù)運(yùn)算的操作
這篇文章主要介紹了java中應(yīng)用Stack進(jìn)行算術(shù)運(yùn)算的操作,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2021-03-03SpringMVC中使用Thymeleaf模板引擎實(shí)例代碼
這篇文章主要介紹了SpringMVC中使用Thymeleaf模板引擎實(shí)例代碼,分享了相關(guān)代碼示例,小編覺得還是挺不錯(cuò)的,具有一定借鑒價(jià)值,需要的朋友可以參考下2018-02-02關(guān)于mybatis的一級緩存和二級緩存的那些事兒
MyBatis自帶的緩存有一級緩存和二級緩存,今天我們就來學(xué)習(xí)一下,文中有非常詳細(xì)的總結(jié),對正在學(xué)習(xí)的小伙伴們很有幫助,需要的朋友可以參考下2021-06-06@PathVariable獲取路徑中帶有 / 斜杠的解決方案
這篇文章主要介紹了@PathVariable獲取路徑中帶有 / 斜杠的解決方案,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-10-10如何用Java將數(shù)據(jù)庫的數(shù)據(jù)生成pdf返回給前端用戶下載
本文詳細(xì)介紹了使用SpringBoot、iText庫、MyBatis等技術(shù)從數(shù)據(jù)庫中選取數(shù)據(jù)并生成PDF文件的后端處理流程,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下2024-09-09