CORS理解
CORS(Cross-Origin Resource Sharing) 跨域资源共享
浏览器在访问当前域名下的资源时,能直接传输数据。但是出于安全考虑,浏览器在访问非当前域名的资源时会遇到跨域访问限制
这时候我们需要一种机制:跨域资源共享(cors)
cors的请求类型
简单请求(Simple Request)
如果一个请求没有包含任何自定义请求头,而且它所使用HTTP动词是GET,HEAD或POST之一,那么它就是一个Simple Request。但是在使用POST作为请求的动词时,该请求的Content-Type需要是application/x-www-form-urlencoded,multipart/form-data或text/plain之一。
预检请求(Preflighted Request)
如果一个请求包含了任何自定义请求头,或者它所使用的HTTP动词是GET,HEAD或POST之外的任何一个动词,那么它就是一个Preflighted Request。如果POST请求的Content-Type并不是application/x-www-form-urlencoded,multipart/form-data或text/plain之一,那么其也是Preflighted Request。
带凭证的请求(Requests with Credential)
一般情况下,一个跨域请求不会包含当前页面的用户凭证。一旦一个跨域请求包含了当前页面的用户凭证,那么其就属于Requests with Credential。
springboot的cors支持
场景
springboot提供api服务(端口8080),前端通过jquery发送http请求(端口63342),由于端口不同,会造成跨域访问
代码
springboot接口
@GetMapping("/api/cors/get")
public String get(){
return "cors test get method";
}
@PostMapping("/api/cors/post")
public String post(){
return "cors test post method";
}
前端代码:cors.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>cors跨域测试</title>
<script src="https://cdn.bootcss.com/jquery/2.1.0/jquery.min.js"></script>
<script type="text/javascript">
function crosGet(){
$.ajax({
type:'get',
url:'http://localhost:8080/api/cors/get',
dateType: 'json',
data:{},
success: function (data) {
$("#getRes").html('请求成功。' + data);
},
error: function (data) {
console.log('请求失败:' + data);
$("#getRes").html('请求失败!!!' + JSON.stringify(data));
}
});
}
function crosPost(){
$.ajax({
type:'post',
url:'http://localhost:8080/api/cors/post',
dateType: 'json',
data:{},
success: function (data) {
$("#postRes").html('请求成功。' + data);
},
error: function (data) {
$("#postRes").html('请求失败!!!' + JSON.stringify(data));
}
});
}
</script>
</head>
<body>
<button onclick="crosGet()">get跨域测试</button>
<div id="getRes" style="width:80%;height:50px;background-color:#ccc;"></div>
<hr/>
<button onclick="crosPost()">post跨域测试</button>
<div id="postRes" style="width:80%;height:50px;background-color:#ccc;"></div>
</body>
</html>
前端代码cors.html可以放到tomcat或者nginx中,这里直接使用idea的open in Browser
打开,默认端口为63342
不允许非当前域名访问错误
启动sprinboot后,点击前端按钮测试,会返回错误:
Access to XMLHttpRequest at 'http://localhost:8080/api/cors/get' from origin 'http://localhost:63342' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
当前域名:http://localhost:63342
要访问的域名:http://localhost:8080
错误原因:发生了跨域访问,服务端(8080)未允许该域名(63342)进行访问
添加cors支持,允许域名访问
springboot中,添加cors支持的配置:
@Configuration
public class CorsConfig {
@Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurer() {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/api/**")
.allowedOrigins("http://localhost:63342");//允许域名访问,如果*,代表所有域名
}
};
}
}
再次测试,成功
header跨域限制
修改配置,只允许header中带Authorization
或者Token
的请求访问
@Configuration
public class CorsConfig {
@Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurer() {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/api/**")
.allowedHeaders("Authorization", "Token")//允许的头信息
.allowedOrigins("http://localhost:63342");
}
};
}
}
修改html中,post接口header带Authorization
,get接口header带aaa
function crosGet(){
$.ajax({
type:'get',
url:'http://localhost:8080/api/cors/get',
dateType: 'json',
data:{},
headers:{'aaa':'Basic 123456'},
success: function (data) {
$("#getRes").html('请求成功。' + data);
},
error: function (data) {
console.log('请求失败:' + data);
$("#getRes").html('请求失败!!!' + JSON.stringify(data));
}
});
}
function crosPost(){
$.ajax({
type:'post',
url:'http://localhost:8080/api/cors/post',
dateType: 'json',
data:{},
//headers:{'Authorization':'Basic 123456'},//带头信息方式1
beforeSend: function (xhr) {
xhr.setRequestHeader('Authorization','Basic 123456');//带头信息方式2
},
success: function (data) {
$("#postRes").html('请求成功。' + data);
},
error: function (data) {
console.log('请求失败:' + data);
$("#postRes").html('请求失败!!!' + JSON.stringify(data));
}
});
}
测试,get访问失败,post成功
请求方式限制
修改配置,只支持post方式访问:
@Configuration
public class CorsConfig {
@Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurer() {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/api/**")
.allowedHeaders("Authorization", "Token")
.allowedMethods("POST")//只允许post方式
.allowedOrigins("http://localhost:63342");
}
};
}
}
测试,get访问失败,post成功
配置get和post都允许,则都访问成功
egistry.addMapping("/api/**")
.allowedMethods("GET", "POST")
局部跨域设置
上面是配置了全局的跨域访问,也可以配置局部访问控制
类上面注解@CrossOrigin
@CrossOrigin(origins = "http://domain.com", allowedHeaders = "token", methods = {RequestMethod.GET, RequestMethod.POST})
@RestController
public class TestResource{
}
方法上面的@CrossOrigin
@RestController
public class TestResource{
@CrossOrigin(origins = "http://domain.com", allowedHeaders = {"header1", "header2"})
@GetMapping("/api/test")
public String test(){
return "test";
}
}
CORS参考文档
源码地址
https://gitee.com/yimingkeji/springboot/tree/master/cors