文章目录
- 1 浏览器的同源策略
- 1.1 什么是源(origin)?
- 1.2 跨域请求?
- 1.3 同源策略(跨域限制)是什么?
- 1.3.1 同源策略的具体限制?
- 1.3.2 浏览器CORS校验
- 2 CORS解决Ajax跨域问题
- 2.1 CORS概述
- 2.2 CORS解决简单请求跨域
- 2.2.1 简单请求
- 2.2.2 简单请求CORS解决方案
- 2.3 CORS解决复杂请求跨域
- 2.3.1 复杂请求
- 2.3.2 预检请求
- 2.3.3 复杂请求CORS解决方案
- 3 Springboot CROS跨域解决方案
- 3.1 配置全局跨域访问
- 3.1.1 继承WebConfigConfigurer
- 3.1.2 配置cors过滤器,新建自己的bean覆盖原来的
- 3.2 配置局部跨域访问
- 3.2.1 @CrossOrigin注解(和controller配合使用)
- 3.2.2 HttpServletResponse中设置header
1 浏览器的同源策略
1.1 什么是源(origin)?
源=协议+域名+端口号
注意点 :
- 不看后面跟着的路径,如
http://localhost:8080/usr/home
的源是http://localhost:8080
http
和https
是两种连接协议
1.2 跨域请求?
首先了解两个概念:
- 所处源:浏览器页面显示的源
- 目标源:请求的服务器源
所处源和目标源不一致就是非同源,就是跨域,因此若前后端跨域,那么前端发送的就是跨域请求。
1.3 同源策略(跨域限制)是什么?
同源策略是浏览器为了确保资源安全而遵循的一种策略,他会对访问的资源进行限制。也就是说,这一策略是浏览器执行的。
需要注意的是:
- 跨域限制仅仅存在浏览器端,服务端不存在跨域限制(两个源不同的服务器之间理论上可以互相请求并接收数据)
- 浏览器对标签跨域没有限制,比如
<src>、<script>、<img>
里面有一些跨域请求是可以正常响应的
1.3.1 同源策略的具体限制?
- Dom限制:源A的脚本无法读取源B的Dom(内嵌iframe可以显示,但是无法通过js代码获取Dom元素)
- Cookie限制:源A无法访问源B的cookie(用户信息安全)
- Ajax响应数据限制:源A可以给源B发送请求,源B也可以正常响应并返回数据,但是响应结果会被浏览器拦截(实际开发最常碰见的问题)
那么,浏览器是如何进行拦截的呢?
1.3.2 浏览器CORS校验
下图是前后端响应时序图,js代码运行在浏览器上,通过浏览器向后端服务器发送ajax请求,服务器正常响应并返回数据,但此时浏览器会做一次校验,校验通过才能正常显示数据,否则会对数据进行拦截并抛出异常。
这个校验的过程就是同源策略执行的时间点,浏览器会根据CORS(Cross-Origin Resource Sharing)规范进行校验,若浏览器和服务器处于同一源则检验通过,否则不通过。
2 CORS解决Ajax跨域问题
2.1 CORS概述
CORS(Cross-Origin Resource Sharing,跨域资源共享)是用于控制浏览器校验跨域请求的一套规范,若要解决跨域限制,服务器需要依照CORS规范,添加特定响应头来控制浏览器校验,大致规则如下:
- 服务器明确表示拒绝跨域请求或没有表示的,则校验不通过
- 服务器表示允许跨域请求,则浏览器校验通过
由于要求服务器在响应体添加相关字段来通过校验,所以服务器必须是自己人,比如无法向百度的接口发送跨域请求,因为人家不认识你,就无法在响应体头里配置源。
2.2 CORS解决简单请求跨域
2.2.1 简单请求
简单请求,需要同时满足以下三个条件:
- 请求方法为GET、HEAD、POST
- 请求头字段符合《CORS安全规范》(只要不修改请求头,如添加字段,基本都符合)
- 请求头的
content-type
必须是‘text/plain’
或‘multipart/form-data’
或‘application/x-www-form-urlencoded’
2.2.2 简单请求CORS解决方案
浏览器在发送请求的时候,请求头里会携带‘origin’
字段标明请求的源,所以服务器若允许跨域请求,只要在响应头(Header)的‘Access-Control-Allow-Origin’
字段添加可以跨域访问的源即可。
如:
//request header:
`……
Origin:http://127.0.0.1:1234
……`
//Response header:
`……
Access-Control-Allow-Origin:http://127.0.0.1:1234
……`
注意:
- 响应体里的访问允许源要完全和请求源一致,不能在末尾多一个斜杠
- localhost和127.0.0.1要区分开,origin和acao保持一致
2.3 CORS解决复杂请求跨域
2.3.1 复杂请求
只要不满足简单请求的任一条件,就是复杂请求。
2.3.2 预检请求
进行复杂请求前会先发送一个预检请求(浏览器自动发起),用来向服务器确认是否允许接下来的【一系列】跨域请求。预检请求是一个options
请求,只有通过了预检请求才会继续发起实际的跨域请求。
预检请求头一般包括:
字段 | 解释 |
---|---|
Origin | 请求源 |
Access-Control-Request-Method | 实际请求(复杂请求本身,非预检请求)的HTTP方法 |
Access-Control-Request-Headers | 实际请求中使用的自定义头(自定义的头字段) |
2.3.3 复杂请求CORS解决方案
Step 1: 服务器首先需要通过浏览器的预检请求,那么需要在响应头里返回如下字段:
字段 | 解释 |
---|---|
Access-Control-Allow-Origin | 允许的源 |
Access-Control-Allow-Methods | 允许的方法 |
Access-Control-Allow-Headers | 允许的自定义头 |
Access-Control-Max-Age | 预检请求的结果缓存时间(可选,设定该字段后,在设定时间内满足访问条件的复杂请求将不再重复发送预检请求,而是直接进行复杂请求) |
Step 2: 处理实际跨域请求,这一步同简单请求,在响应头‘Access-Control-Allow-Origin’
字段添加源
3 Springboot CROS跨域解决方案
介绍了允许跨域请求的原理后,就可以根据相关字段在后端进行配置,以通过同源策略。
3.1 配置全局跨域访问
3.1.1 继承WebConfigConfigurer
新建跨域config类,继承WebConfigConfigurer,重写addCorsMappings方法:
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
//添加映射路径,哪些请求的url路径可以进行处理,一般会将url和controller进行绑定,"/**"则表示所有路径都进行处理
registry.addMapping("/**")
//表示允许所有的域都可以请求
.allowedOrigins("*")
//表示在3600秒内不需要再发送预校验请求
.maxAge(3600)
//是否发送Cookie信息
.allowCredentials(true)
//表示允许跨域请求的方法
.allowedMethods("GET","POST", "PUT", "DELETE")
//表示允许跨域请求包含content-type
.allowedHeaders("*")
//暴露哪些头部信息(因为跨域访问默认不能获取全部头部信息,如果需要获取则在exposedHeaders里定义好需要暴露的字段)
.exposedHeaders("Header1", "Header2");
}
}
3.1.2 配置cors过滤器,新建自己的bean覆盖原来的
@Configuration
public class CorsFilterConfig {
@Bean
public CorsFilter corsFilter() {
CorsConfiguration config = new CorsConfiguration();
config.addAllowedOrigin("*");
config.setAllowCredentials(true);
config.addAllowedMethod("*");
config.addAllowedHeader("*");
config.addExposedHeader("*");
UrlBasedCorsConfigurationSource configSource = new UrlBasedCorsConfigurationSource();
configSource.registerCorsConfiguration("/**", config);
return new CorsFilter(configSource);
}
}
3.2 配置局部跨域访问
3.2.1 @CrossOrigin注解(和controller配合使用)
注解常用属性(若不加属性则表示没有限制):
字段 | 解释 |
---|---|
origins | 指定允许访问的源 |
allowedHeaders | 指定允许的请求头 |
allowedMethods | 指定允许的HTTP方法 |
exposedHeaders | 指定允许暴露的响应头 |
allowCredentials | 指定是否允许发送身份验证信息(如cookie) |
maxAge | 指定预检请求(OPTIONS)的缓存时间,单位为秒 |
用在类上:
则该类所有方法都可以被允许的域访问:
@Controller
@CrossOrigin("http://localhost:1234")
public class EmpController {
@RequestMapping("/test")
public String getTestInfo() {
return “testinfo”;
}
@RequestMapping("/test2")
public String getTestInfo2() {
return “testinfo2”;
}
}
用在方法上:
没有配置@CrossOrigin的方法无法进行跨域访问:
@Controller
public class EmpController {
//可以跨域访问
@CrossOrigin("http://localhost:1234")
@RequestMapping("/test")
public String getTestInfo() {
return “testinfo”;
}
//不可以跨域访问
@RequestMapping("/test2")
public String getTestInfo2() {
return “testinfo2”;
}
}
3.2.2 HttpServletResponse中设置header
每个都要设置,太麻烦。
@RequestMapping("/test")
@ResponseBody
public String test(HttpServletResponse response){
response.addHeader("Access-Control-Allow-Origin", "*");
return "test";
}