一、前言

HTML 5中新增的跨域资源访问(Cross-Origin Resource Sharing)特性可以让我们在开发后端系统的时候决定资源是否允许被跨域访问。所谓跨域指的是域名不同或者端口不同或者协议不同,比如当从 wno704.com 网站访问 wno704.com:8080 网站资源就会存在跨域问题。Spring从4.2版本开始就提供了跨域的支持,开箱即用。这里介绍如何在Spring Boot开发中解决跨域的问题,主要分为注解驱动和接口编程的方式。

二、模拟跨域

要解决跨域问题,我们就得先模拟一个跨域情景。

2.1 引入依赖:

1
2
3
4
5
6
7
8
9
        <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>

2.2 创建conroller

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Controller
public class TestController {

@RequestMapping("index")
public String index () {
return "index";
}

@RequestMapping("hello")
@ResponseBody
public String hello(){
return "hello";
}
}

2.3 创建页面

然后在resources/templates下新建index.html:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>跨域测试</title>
<script src="https://cdn.staticfile.org/jquery/1.10.2/jquery.min.js"></script>
</head>
<body>
<div id="hello"></div>
<script type="text/javascript">
$(function () {
$.get("http://www.wno704.com:8080/hello", function (data) {
$("#hello").text(data);
})
});
</script>
</body>
</html>

2.4 测试

编辑本地hosts文件,将http://www.wno704.com网址映射到127.0.0.1上:

启动项目访问Mhttp://localhost:8080/>,会发现页面并没有成功显示hello,并且F12观察浏览器控制台会发现其报错了:

这是因为我们在http://localhost:8080/域名下试图访问http://www.wno704.com:8080下的hello接口,这就存在跨域问题,接下来我们来解决这个问题。

三、注解驱动

Spring 4.2后提供了@CrossOrigin注解,该注解可以标注于方法或者类上,包含了以下属性:

属性含义
value指定所支持域的集合,表示所有域都支持,默认值为。这些值对应HTTP请求头中的Access-Control-Allow-Origin
origins同value
allowedHeaders允许请求头中的header,默认都支持
exposedHeaders响应头中允许访问的header,默认为空
methods支持请求的方法,比如GET,POST,PUT等,默认和Controller中的方法上标注的一致。
allowCredentials是否允许cookie随请求发送,使用时必须指定具体的域
maxAge预请求的结果的有效期,默认30分钟

我们来改造TestController中的hello方法:

1
2
3
4
5
6
    @CrossOrigin(value = "*")
@RequestMapping("hello")
@ResponseBody
public String hello(){
return "hello";
}

表示允许所有域都支持,重启项目,再次访问 http://localhost:8080/

四、接口编程

除了使用@CrossOrigin注解外,我们可以使用接口编程的方式进行统一配置。

新建config包,然后创建WebConfig,实现WebMvcConfigurer,重写addCorsMappings默认实现:

1
2
3
4
5
6
7
8
9
10
11
@Configuration
public class WebConfig implements WebMvcConfigurer {

@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*")
.allowedMethods("GET");
}

}

上面配置表示允许所有请求支持跨域访问,并且不限定域,但是支持持GET方法。将hello方法上的@CrossOrigin注解注释掉,重启项目,再次访问 http://localhost:8080/ ,结果也是OK的。

五、过滤器实现

查看官方文档,发现其还提供了基于过滤器的实现方式:

1
2
3
4
5
6
7
8
9
10
11
    @Bean
public FilterRegistrationBean corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true);
config.addAllowedOrigin("*");
source.registerCorsConfiguration("/**", config);
FilterRegistrationBean bean = new FilterRegistrationBean(new CorsFilter(source));
bean.setOrder(0);
return bean;
}

六、Actuator跨域

如果项目里集成了Actuator相关功能,其暴露的接口也支持跨域,只需要在配置文件中添加如下配置即可:

ENDPOINTS CORS CONFIGURATION (CorsEndpointProperties)

management.endpoints.web.cors.allow-credentials= # Whether credentials are supported. When not set, credentials are not supported.
management.endpoints.web.cors.allowed-headers= # Comma-separated list of headers to allow in a request. '' allows all headers.
management.endpoints.web.cors.allowed-methods= # Comma-separated list of methods to allow. '
' allows all methods. When not set, defaults to GET.
management.endpoints.web.cors.allowed-origins= # Comma-separated list of origins to allow. '*' allows all origins. When not set, CORS support is disabled.
management.endpoints.web.cors.exposed-headers= # Comma-separated list of headers to include in a response.
management.endpoints.web.cors.max-age=1800s # How long the response from a pre-flight request can be cached by clients. If a duration suffix is not specified, seconds will be used.