一、搭建服務(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("接收到跨域請(qǐng)求參數(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、頁(yè)面
<!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>跨域測(cè)試頁(yè)面</title>
<script src="js/jquery.min.js"></script>
</head>
<body>
<h1>跨域測(cè)試</h1>
<div>
<input type="button" onclick="mySubmit()" value=" 發(fā)送跨域請(qǐng)求 ">
</div>
<script>
function mySubmit() {
// 發(fā)送跨域請(qǐng)求
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,訪問(wèn)此頁(yè)面可以打開(kāi)如下測(cè)試頁(yè)面:
2.5、跨域請(qǐng)求結(jié)果
??在沒(méi)有任何跨域處理的情況下,我們點(diǎn)擊按鈕,發(fā)起請(qǐng)求,得到結(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、常見(jiàn)跨域情況
請(qǐng)求地址 | 說(shuō)明 | 是否跨域 |
---|---|---|
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 |
同頂級(jí)域名不同二級(jí)域名 | 是 |
http://www.alian.com/a http://www.yang.com/a |
不同域名 | 是 |
http://www.alian.com/a http://10.130.1.88:80/a |
域名和對(duì)應(yīng)的ip | 是 |
三、解決方案
3.1、通過(guò) @CrossOrigin 注解
??最簡(jiǎn)單的方式是通過(guò)使用注解@CrossOrigin,但是需要注意的是這個(gè)注解版本,這個(gè)注解是從Spring Framework 4.2 GA開(kāi)始有的。如果你項(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("接收到跨域請(qǐng)求參數(shù):{}",request.getParameter("name"));
return "Request CrossServer Success";
}
}
3.2、通過(guò)配置類
??為了避免方法間相互影響,我們先在我們的方法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("*");
}
}
?? 上面是對(duì)全部方法都支持跨域,我們也可以指定某個(gè)請(qǐng)求支持跨域,如下:
@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、通過(guò) CorsFilter 對(duì)象
??再?zèng)]有使用以上方式解決跨域的情況下,我們使用過(guò)濾器也可以實(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配置對(duì)象
CorsConfiguration config = new CorsConfiguration();
// 支持域
config.addAllowedOriginPattern("*");
// 是否發(fā)送 Cookie
config.setAllowCredentials(true);
// 支持請(qǐng)求方式
config.addAllowedMethod("*");
// 允許的原始請(qǐng)求頭部信息
config.addAllowedHeader("*");
// 暴露的頭部信息
config.addExposedHeader("*");
// 添加地址映射
UrlBasedCorsConfigurationSource corsConfigurationSource = new UrlBasedCorsConfigurationSource();
corsConfigurationSource.registerCorsConfiguration("/**", config);
// 返回 CorsFilter 對(duì)象
return new CorsFilter(corsConfigurationSource);
}
}
??上面是對(duì)所有域名請(qǐng)求都支持跨域,我們也可以指定某個(gè)域名,意思就是只有指定域名可以跨域請(qǐng)求。比如:我們只允許http://localhost:8081的跨域請(qǐng)求。
@Configuration
public class CustomCorsFilter {
@Bean
public CorsFilter corsFilter() {
// 創(chuàng)建cors配置對(duì)象
CorsConfiguration config = new CorsConfiguration();
// 支持指定域
config.addAllowedOriginPattern("http://localhost:8081");
// 是否發(fā)送 Cookie
config.setAllowCredentials(true);
// 支持請(qǐng)求方式
config.addAllowedMethod("*");
// 允許的原始請(qǐng)求頭部信息
config.addAllowedHeader("*");
// 暴露的頭部信息
config.addExposedHeader("*");
// 添加地址映射
UrlBasedCorsConfigurationSource corsConfigurationSource = new UrlBasedCorsConfigurationSource();
corsConfigurationSource.registerCorsConfiguration("/**", config);
// 返回 CorsFilter 對(duì)象
return new CorsFilter(corsConfigurationSource);
}
}
3.4、通過(guò) Response 對(duì)象
??在沒(méi)有使用以上方式解決跨域的情況下,我們通過(guò)Response對(duì)象也可以實(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("接收到跨域請(qǐng)求參數(shù):{}",request.getParameter("name"));
return "Request CrossServer Success";
}
}
??
3.5、通過(guò)實(shí)現(xiàn) ResponseBodyAdvice 對(duì)象
??如果是嫌上述方法在每個(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)容是否需要重寫(xiě)(返回true表示重寫(xiě))
*
* @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:對(duì) response 進(jìn)行具體的處理
3.6、通過(guò) 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接收到跨域請(qǐng)求參數(shù):姓名:{},年齡:{}",request.getParameter("name"),request.getParameter("age"));
log.info("2接收到跨域請(qǐng)求參數(shù):{}",callback);
String json="{\"msg\":\"Request CrossServer Success\"}";
return callback+"("+ json +")";
}
}
??這里需要注意的兩個(gè)點(diǎn):
- 一個(gè)是請(qǐng)求的方法得支持GET方法(@RequestMapping就是POST、GET都支持)
- 返回的結(jié)果轉(zhuǎn)為json字符串,作為回調(diào)方法的參數(shù)
3.6.2、前端方法一
??對(duì)應(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>跨域接口測(cè)試</h1>
<div>
<input type="button" onclick="crossRequest()" value="發(fā)送跨域請(qǐng)求">
<input type="button" onclick="crossCallBack()" value="老版本發(fā)送跨域請(qǐng)求">
</div>
<script>
function crossRequest() {
// 發(fā)送跨域請(qǐng)求
jQuery.ajax({
url: "http://localhost:8080/crossServer/cross/request",
type: "POST",
data: {"name": "alian"},
success: function (result) {
alert("請(qǐng)求返回?cái)?shù)據(jù):" + result);
}
});
}
// 只看這個(gè)方法
function crossCallBack() {
// 發(fā)送跨域請(qǐng)求
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>跨域接口測(cè)試</h1>
<div>
<input type="button" onclick="crossRequest()" value="發(fā)送跨域請(qǐng)求">
<input type="button" onclick="crossCallBack()" value="老版本發(fā)送跨域請(qǐng)求">
</div>
<script>
function crossRequest() {
// 發(fā)送跨域請(qǐng)求
jQuery.ajax({
url: "http://localhost:8080/crossServer/cross/request",
type: "POST",
data: {"name": "alian"},
success: function (result) {
alert("請(qǐng)求返回?cái)?shù)據(jù):" + result);
}
});
}
// 看這個(gè)方法
function crossCallBack() {
// 發(fā)送跨域請(qǐng)求
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)方法了,不會(huì)進(jìn)入到success了
function jsonpCallBack(data){
var result = JSON.stringify(data); //json對(duì)象轉(zhuǎn)成字符串
alert("Jsonp請(qǐng)求返回?cái)?shù)據(jù):" + result);
}
</script>
</body>
</html>
- 首先你得指定你的jsonp方法,比如我這里是jsonpCallBack,也就是回調(diào)方法
- 同時(shí)我們請(qǐng)求里需要把回調(diào)方法名傳遞到后臺(tái),比如我這里callback=jsonpCallBack
- 回調(diào)就會(huì)直接進(jìn)入到我們的jsonpCallBack方法
??jsonpCallBack是自定義方法名,你也可以改成其他的名字,記得請(qǐng)求參數(shù),請(qǐng)求地址,及其方法定義,三個(gè)一起改哦。
??兩者的結(jié)果如下:
文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-488768.html
總結(jié)
??跨域是瀏覽器的行為,跨域問(wèn)題其實(shí)就是瀏覽器的同源策略所導(dǎo)致的,所以后端就沒(méi)有跨域一說(shuō)了。以上幾種跨域解決方法單獨(dú)用一個(gè)就行了,盡量不要混用,以免出現(xiàn)其他重復(fù)沖突的問(wèn)題。還有其他一些通過(guò)nginx或者網(wǎng)關(guān)等方法都可以解決跨域問(wèn)題,就不一一介紹了。文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-488768.html
到了這里,關(guān)于Java實(shí)現(xiàn)后端跨域的常見(jiàn)解決方式的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!