上一章简单介绍了SpringBoot全局异常处理(三十),如果没有看过,​​请观看上一章​​

本章节参考 江南一点雨大神的文章: ​​Spring Boot2 系列教程(十四)CORS 解决跨域问题​

一. 跨域问题

一.一 浏览器的同源策略

同源策略是由 Netscape 提出的一个著名的安全策略,它是浏览器最核心也最基本的安全功能,
现在所有支持 JavaScript 的浏览器都会使用这个策略。

所谓同源是指协议、域名以及端口要相同。

同源策略是基于安全方面的考虑提出来的,这个策略本身没问题,但是我们在实际开发中,

由于各种原因又经常有跨域的需求,传统的跨域方案是 JSONP,JSONP 虽然能解决跨域但是有一个很大的局限性,
那就是只支持 GET 请求,

不支持其他类型的请求,而今天我们说的 CORS(跨域源资源共享)
(CORS,Cross-origin resource sharing)是一个 W3C 标准,

它是一份浏览器技术的规范,提供了 Web 服务从不同网域传来沙盒脚本的方法,以避开浏览器的同源策略,
这是 JSONP 模式的现代版。

一.二 验证同源策略,复现跨域问题

创建两个项目,一个是服务的提供者, SpringBoot_Cors_Provider (服务提供者),
SpringBoot_Cors_Consumer (服务的消费者),
这两个项目均是 SpringBoot整合Thymeleaf 的相关项目

一.二.一 服务提供者

端口号是 8081, 服务路径为: Cors_Provider

一.二.一.一 提供服务
@Controller
public class InfoController {
/**
* 跳转到主页
* @param model
* @return
*/
@RequestMapping("/index")
public String info(Model model){
return "index";
}
@GetMapping("/findById")
@ResponseBody
public User findById(Integer id){
User user=new User();
user.setId(id);
user.setName("两个蝴蝶飞");
user.setDescription("Get 提交 跨域问题");
return user;
}
@PostMapping("/addUser")
@ResponseBody
public User addUser(@RequestBody User user){
//补充信息
user.setName("岳泽霖");
user.setDescription("POST提交 跨域问题");
return user;
}
}
一.二.一.二 首页 index.html
<body class="container">

<!--展示信息-->
提供者: <p id="message" style="margin-top:200px;"></p>

<button type="button" class="btn btn-success" id="get_submit">Get获取信息</button>

<button type="button" class="btn btn-success" id="post_submit">Post获取信息</button>
<script type="text/javascript" src="webjars/jquery/3.5.1/jquery.js"></script>
<script type="text/javascript" src="webjars/bootstrap/3.4.1/js/bootstrap.js"></script>

<script>
$(function(){

})
//get请求
$("#get_submit").click(function(){
$.ajax({
async:false,
type:"get",
url:"findById?id=1",
success:function(data){
$("#message").text(
JSON.stringify(data)
);
}
});
});

//post 请求
$("#post_submit").click(function(){
$.ajax({
async:false,
type:"post",
url:"addUser",
data:JSON.stringify({
"id":2
}),
dataType:"json",
contentType:"application/json;charset=utf-8",
success:function(data){
$("#message").text(
JSON.stringify(data)
);
}
});
});
</script>
</body>
一.二.一.三 服务提供者验证

get 请求

SpringBoot通过Cors解决跨域问题(三十一)_SpringBoot Cors

post 请求

SpringBoot通过Cors解决跨域问题(三十一)_同源策略_02

一.二.二 服务消费者

端口号是 8082, 项目路径是 Cors_Consumer

只需要一个前端的静态页面 index.html 和 一个跳转方法即可。

一.二.二.一 controller层跳转
@Controller
public class InfoController {
/**
* 普通展示
* @param model
* @return
*/
@RequestMapping("/index")
public String info(Model model){
return "index";
}

}
一.二.二.二 首页 index.html
<body class="container">

<!--展示信息-->
消费者: <p id="message" style="margin-top:200px;"></p>

<button type="button" class="btn btn-success" id="get_submit">Get获取信息</button>

<button type="button" class="btn btn-success" id="post_submit">Post获取信息</button>
<script type="text/javascript" src="webjars/jquery/3.5.1/jquery.js"></script>
<script type="text/javascript" src="webjars/bootstrap/3.4.1/js/bootstrap.js"></script>

<script>
$(function(){

})
//get请求
$("#get_submit").click(function(){
$.ajax({
async:false,
type:"get", //填写具体的路径
url:"http://localhost:8081/Cors_Provider/findById?id=1",
success:function(data){
$("#message").text(
JSON.stringify(data)
);
}
});
});

//post 请求
$("#post_submit").click(function(){
$.ajax({
async:false,
type:"post", //填写具体的路径
url:"http://localhost:8081/Cors_Provider/addUser",
data:JSON.stringify({
"id":2
}),
dataType:"json",
contentType:"application/json;charset=utf-8",
success:function(data){
$("#message").text(
JSON.stringify(data)
);
}
});
});
</script>
</body>
一.二.二.三 跨域请求

SpringBoot通过Cors解决跨域问题(三十一)_json_03

SpringBoot通过Cors解决跨域问题(三十一)_Cors跨域处理_04

CORS error

SpringBoot通过Cors解决跨域问题(三十一)_同源策略_05

端口号不同,不属于同源,所以无法访问。

注意,是浏览器无法访问, 如果是用 Postman 的话,是可以正常请求通过的

如果是后端之间通过 RestTemplate, Feign 等相互调用,也是可以访问到的。

(所以,不要说,我接口 postman没有问题啊,你前端怎么访问不到呢)

二. Cors 解决跨域文案

针对的都是服务提供者端, SpringBoot_Cors_Provider 项目里面进行操作.

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface CrossOrigin {
@Deprecated
String[] DEFAULT_ORIGINS = {"*"};

@Deprecated
String[] DEFAULT_ALLOWED_HEADERS = {"*"};

@Deprecated
boolean DEFAULT_ALLOW_CREDENTIALS = false;

@Deprecated
long DEFAULT_MAX_AGE = 1800;
@AliasFor("origins")
String[] value() default {};
@AliasFor("value")
String[] origins() default {};
String[] allowedHeaders() default {};
String[] exposedHeaders() default {};
RequestMethod[] methods() default {};
String allowCredentials() default "";
long maxAge() default -1;

}

可以在类上,也可以在方法上.

通常使用 origins 属性 和 maxAge 属性, 为-1 表示永不过期.

二.一 单方法 单类处理

二.一.一 单方法

添加 @CrossOrigin 注解, 用 origins 属性来指定

//端口号后面不能跟  /
@CrossOrigin(origins = "http://localhost:8082")
@GetMapping("/findById")
@ResponseBody
public User findById(Integer id){
User user=new User();
user.setId(id);
user.setName("两个蝴蝶飞");
user.setDescription("Get 提交 跨域问题");
return user;
}

只能是 http 协议, localhost 主机, 8082 端口的请求可以访问

重启服务,再次请求,就可以获取到了

SpringBoot通过Cors解决跨域问题(三十一)_同源策略_06

@PostMapping("/addUser")
@ResponseBody
@CrossOrigin(origins = "*")
public User addUser(@RequestBody User user){
//补充信息
user.setName("岳泽霖");
user.setDescription("POST提交 跨域问题");
return user;
}

origins="*" ,表示所有的协议,所有的主机 ,所有的端口 均可以访问.

重启服务器,再次进行请求

SpringBoot通过Cors解决跨域问题(三十一)_跨域处理_07

是可以的。

这是针对每一个方法的。 如果一个类里面,方法过多,显然这种方式是不合理的.

二.一.二 单类

在类上添加该注解

@Controller
@CrossOrigin(origins = "*")
public class InfoController {

}

则这个类下面的所有的方法,都可以 进行跨域访问。

当类过多时,这种方式也不太友好。

二.二 CorsConfig 配置全局性跨域

在 config 目录下,创建 CorsConfig 配置类

@Configuration
public class CorsConfig {
@Bean
public FilterRegistrationBean corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true);
//设置你要允许的网站域名,如果全允许则设为 *
config.addAllowedOrigin("*");
//config.addAllowedOrigin("http://localhost:8082");
//设置要限制的Header和Method
config.addAllowedHeader("*");
config.addAllowedMethod("*");
source.registerCorsConfiguration("/**", config);
FilterRegistrationBean bean = new FilterRegistrationBean(new CorsFilter(source));
//bean注册顺序
bean.setOrder(0);
return bean;
}
}

同样是可以的.

SpringBoot通过Cors解决跨域问题(三十一)_同源策略_08

二.三 添加 Cors映射

在 WebConfig 目录下, 添加相应的 cors 映射 配置

@Configuration
public class WebConfig extends WebMvcConfigurerAdapter {
/**
* 配置静态的资源信息
* @param registry
*/
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("doc.html").addResourceLocations("classpath:/META-INF/resources/");
registry.addResourceHandler("swagger-ui.html").addResourceLocations("classpath:/META-INF/resources/");
//映射 static 目录
registry.addResourceHandler("/static/**").addResourceLocations("classpath:/static/");
//放置其他 业务页面资源
registry.addResourceHandler("/**").addResourceLocations("classpath:/templates/");
}
/**
进行添加配置
*/
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
//是否发送Cookie
.allowCredentials(true)
//放行哪些原始域
.allowedOrigins("*")
.allowedMethods("*")
.allowedHeaders("*")
.allowCredentials(true);
}
}

同样是可以的

SpringBoot通过Cors解决跨域问题(三十一)_json_09

但是,通过这种方式配置的话, 自定义的拦截器会不生效。

一般采用的,都是第二种方式 CorsConfig

本章节的代码放置在 github 上:

​https://github.com/yuejianli/springboot/tree/develop/SpringBoot_Cors_Provider​

​https://github.com/yuejianli/springboot/tree/develop/SpringBoot_Cors_Consumer​

谢谢您的观看,如果喜欢,请关注我,再次感谢 !!!