什么是跨域
CORS(Cross-Origin Resource Sharing)"跨域资源共享",是一个W3C标准,它允许浏览器向跨域服务器发送XMLHttpRequest请求,打破了Ajax只能访问本站内资源的同源限制,CORS在很多地方都有被使用,微信支付的JS支付就是通过JS向微信服务器发送跨域请求。
如果在A网站中,我们希望使用Ajax来获得B网站中的特定内容,如果A网站与B网站不在同一个域中(同一协议,同一IP地址,同一端口),那么就出现了跨域访问问题,下面我们来讲一讲如何解决跨域问题。
1.创建SpringBoot项目,预先添加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.实现WebMvcConfigurer接口
Spring Boot 2.0中已经废弃WebMvcConfigurerAdapter类, 开发人员可以通过实现WebMvcConfigurer接口实现相应的功能
@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:允许所有的请求方法访问该跨域资源服务器,如:POST、GET、PUT、DELETE等。
- allowedOrigins:允许所有的请求域名访问我们的跨域资源,可以固定单条或者多条内容,如:"http://www.baidu.com",只有百度可以访问我们的跨域资源。
- allowedHeaders:允许所有的请求header访问,可以自定义设置任意请求头信息,如:"X-YAUTH-TOKEN"
3.测试跨域请求
创建一个测试跨域资源的控制器,这里仅仅添加了一个测试返回文本的内容,当然这个控制器可以处理任意业务逻辑。
@RestController
public class BaseContentler {
@RequestMapping(value = "/req")
public String index(){
return "跨域访问请求成功!";
}
}
在项目外创建一个index.html页面,页面内添加部分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路径的文本内容,证明我们的ajax请求通过跨域资源库访问了开放跨域的资源路径
4.其他跨域的解决方案
前端解决方案
- 1.使用JSONP方式实现跨域调用;
- 2.使用NodeJS服务器做为服务代理,前端发起请求到NodeJS服务器, NodeJS服务器代理转发请求到后端服务器;
- 3.设置浏览器允许跨域访问,如Chrome浏览器设置--disable-web-security属性, 该方案仅适用于开发环境 下的开发调试。
后端解决方案
使用@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过滤器来过滤服务请求,向请求端设置Response Header(响应头部)的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如何设置允许跨域
设置允许所有域名跨域:
app.all("*",function(req,res,next){
//设置允许跨域的域名,*代表允许任意域名跨域
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尝试请求快速结束
else
next();
}
设置允许指定域名“http://www.zhangpeiyue.com”跨域:
app.all("*",function(req,res,next){
//设置允许跨域的域名,*代表允许任意域名跨域
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尝试请求快速结束
else
next();
}
设置允许多个域名跨域:
app.all("*",function(req,res,next){
if( req.headers.origin.toLowerCase() == "http://www.zhangpeiyue.com"
|| req.headers.origin.toLowerCase() =="http://127.0.0.1" ) {
//设置允许跨域的域名,*代表允许任意域名跨域
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尝试请求快速结束
else
next();
}
如果允许的域名较多,可以将允许跨域的域名放到数组当中:
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())){
//设置允许跨域的域名,*代表允许任意域名跨域
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尝试请求快速结束
else
next();
}