
什么是跨域
CORS(Cross-Origin Resource Sharing)"跨域資源共享",是一個W3C標準,它允許瀏覽器向跨域服務(wù)器發(fā)送XMLHttpRequest請求,打破了Ajax只能訪問本站內(nèi)資源的同源限制,CORS在很多地方都有被使用,微信支付的JS支付就是通過JS向微信服務(wù)器發(fā)送跨域請求。
如果在A網(wǎng)站中,我們希望使用Ajax來獲得B網(wǎng)站中的特定內(nèi)容,如果A網(wǎng)站與B網(wǎng)站不在同一個域中(同一協(xié)議,同一IP地址,同一端口),那么就出現(xiàn)了跨域訪問問題,下面我們來講一講如何解決跨域問題。
1.創(chuàng)建SpringBoot項目,預(yù)先添加Web依賴
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
2.實現(xiàn)WebMvcConfigurer接口
Spring Boot 2.0中已經(jīng)廢棄WebMvcConfigurerAdapter類, 開發(fā)人員可以通過實現(xiàn)WebMvcConfigurer接口實現(xiàn)相應(yīng)的功能
@Configuration
public class CORSConfiguration implements WebMvcConfigurer{
@Override
public void addCorsMappings(CorsRegistry registry){
registry.addMapping("/**")
.allowedMethods("GET", "POST", "DELETE", "PUT","PATCH")
.allowedOrigins("*")
.allowedHeaders("*")
.allowCredentials(true)
.maxAge(3600);
}
}
簡單介紹下配置信息
- addMapping:配置可以被跨域的路徑,可以任意配置,可以具體到直接請求路徑。
- allowedMethods:允許所有的請求方法訪問該跨域資源服務(wù)器,如:POST、GET、PUT、DELETE等。
- allowedOrigins:允許所有的請求域名訪問我們的跨域資源,可以固定單條或者多條內(nèi)容,如:"http://www.baidu.com",只有百度可以訪問我們的跨域資源。
- allowedHeaders:允許所有的請求header訪問,可以自定義設(shè)置任意請求頭信息,如:"X-YAUTH-TOKEN"
3.測試跨域請求
創(chuàng)建一個測試跨域資源的控制器,這里僅僅添加了一個測試返回文本的內(nèi)容,當然這個控制器可以處理任意業(yè)務(wù)邏輯。
@RestController
public class BaseContentler {
@RequestMapping(value = "/req")
public String index(){
return "跨域訪問請求成功!";
}
}
在項目外創(chuàng)建一個index.html頁面,頁面內(nèi)添加部分jquery代碼
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>跨域資源訪問</title>
<script type="text/javascript" src="https://code.jquery.com/jquery-3.2.1.min.js"></script>
<script type="text/javascript">
$(function () {
$("#cors").click(function () {
$.ajax({
url:"http://127.0.0.1:8080/req",
success:function (data) {
alert(data);
}
})
});
});
</script>
</head>
<body>
<input type="button" id="cors" value="跨域資源訪問"/>
</body>
</html>
打開index.html點擊按鈕,界面返回/req路徑的文本內(nèi)容,證明我們的ajax請求通過跨域資源庫訪問了開放跨域的資源路徑
4.其他跨域的解決方案
前端解決方案
- 1.使用JSONP方式實現(xiàn)跨域調(diào)用;
- 2.使用NodeJS服務(wù)器做為服務(wù)代理,前端發(fā)起請求到NodeJS服務(wù)器, NodeJS服務(wù)器代理轉(zhuǎn)發(fā)請求到后端服務(wù)器;
- 3.設(shè)置瀏覽器允許跨域訪問,如Chrome瀏覽器設(shè)置--disable-web-security屬性, 該方案僅適用于開發(fā)環(huán)境 下的開發(fā)調(diào)試。
后端解決方案
使用@CrossOrigin注解聲明類和方法允許跨域訪問
@RestController
public class BaseContentler {
@RequestMapping(value = "/req")
@CrossOrigin
public String index(){
return "跨域訪問請求成功!";
}
}
繼承使用Spring Web的CorsFilter(適用于Spring MVC、Spring Boot)
import org.springframework.stereotype.Component;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
import java.util.Arrays;
@Component
public class CustomCorsFilter extends CorsFilter {
public CustomCorsFilter() {
super(configurationSource());
}
private static UrlBasedCorsConfigurationSource configurationSource() {
CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true);
config.addAllowedOrigin("*");
config.addAllowedHeader("*");
config.setMaxAge(36000L);
config.setAllowedMethods(Arrays.asList("GET", "HEAD", "POST", "PUT", "DELETE", "OPTIONS"));
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/user/**", config);
return source;
}
}
使用Filter過濾器來過濾服務(wù)請求,向請求端設(shè)置Response Header(響應(yīng)頭部)的Access-Control-Allow-Origin屬性聲明允許跨域訪問
@WebFilter
public class CorsFilter implements Filter {
@Override
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");
chain.doFilter(req, res);
}
public void init(FilterConfig filterConfig) {
}
public void destroy() {
}
}
Node.js如何設(shè)置允許跨域
設(shè)置允許所有域名跨域:
app.all("*",function(req,res,next){
//設(shè)置允許跨域的域名,*代表允許任意域名跨域
res.header("Access-Control-Allow-Origin","*");
//允許的header類型
res.header("Access-Control-Allow-Headers","content-type");
//跨域允許的請求方式
res.header("Access-Control-Allow-Methods","DELETE,PUT,POST,GET,OPTIONS");
if (req.method.toLowerCase() == 'options')
res.send(200); //讓options嘗試請求快速結(jié)束
else
next();
}
設(shè)置允許指定域名“http://www.zhangpeiyue.com”跨域:
app.all("*",function(req,res,next){
//設(shè)置允許跨域的域名,*代表允許任意域名跨域
res.header("Access-Control-Allow-Origin","http://www.zhangpeiyue.com");
//允許的header類型
res.header("Access-Control-Allow-Headers","content-type");
//跨域允許的請求方式
res.header("Access-Control-Allow-Methods","DELETE,PUT,POST,GET,OPTIONS");
if (req.method.toLowerCase() == 'options')
res.send(200); //讓options嘗試請求快速結(jié)束
else
next();
}
設(shè)置允許多個域名跨域:
app.all("*",function(req,res,next){
if( req.headers.origin.toLowerCase() == "http://www.zhangpeiyue.com"
|| req.headers.origin.toLowerCase() =="http://127.0.0.1" ) {
//設(shè)置允許跨域的域名,*代表允許任意域名跨域
res.header("Access-Control-Allow-Origin", req.headers.origin);
}
//允許的header類型
res.header("Access-Control-Allow-Headers", "content-type");
//跨域允許的請求方式
res.header("Access-Control-Allow-Methods", "DELETE,PUT,POST,GET,OPTIONS");
if (req.method.toLowerCase() == 'options')
res.send(200); //讓options嘗試請求快速結(jié)束
else
next();
}
如果允許的域名較多,可以將允許跨域的域名放到數(shù)組當中:
app.all("*",function(req,res,next){
var orginList=[
"http://www.zhangpeiyue.com",
"http://www.alibaba.com",
"http://www.qq.com",
"http://www.baidu.com"
]
if(orginList.includes(req.headers.origin.toLowerCase())){
//設(shè)置允許跨域的域名,*代表允許任意域名跨域
res.header("Access-Control-Allow-Origin",req.headers.origin);
}
//允許的header類型
res.header("Access-Control-Allow-Headers", "content-type");
//跨域允許的請求方式
res.header("Access-Control-Allow-Methods", "DELETE,PUT,POST,GET,OPTIONS");
if (req.method.toLowerCase() == 'options')
res.send(200); //讓options嘗試請求快速結(jié)束
else
next();
}