1,概念
跨域并不是请求发不出去,请求能发出去,服务端能收到请求并正常返回结果,只是结果被浏览器拦截了。
跨域问题的产生是由于浏览器的同源策略。
同源策略(Same-Origin Policy)是一种浏览器安全策略,用于限制在浏览器中加载的网页脚本与来自不同源的资源进行交互。同源指的是协议(如 HTTP 或 HTTPS)、域名和端口号三者完全相同。
同源策略的目的是保护用户的信息安全,防止恶意网站通过跨域请求访问用户的数据。如果没有同源策略的限制,恶意网站可能会利用用户的浏览器发送跨域请求,获取用户的敏感信息。
同源策略涵盖了以下几个方面的限制:
- Cookie、LocalStorage 和 IndexedDB 等客户端存储的读取限制:脚本只能访问与其来源相同的存储区域,无法读取其他网站的数据。
- DOM 访问限制:脚本只能访问与其来源相同的 DOM 元素,无法直接操作其他网站的页面元素。
- XMLHttpRequest 和 Fetch API 的跨域请求限制:默认情况下,这些网络请求只能发送到与脚本来源相同的域名。
- 媒体数据的限制:音频和视频等多媒体资源的播放,受到了同源策略的限制。
有三个标签是允许跨域加载资源:
- <img src=XXX>
- <link href=XXX>
- <script src=XXX>
2,CORS
cross-origin resource sharing, 跨域资源共享。
CORS需要浏览器和服务器同时支持。目前,所有主流浏览器都支持该功能,IE浏览器不能低于IE10。在浏览器端, 整个CORS通信过程,都是浏览器自动完成,不需要用户参与。对于开发者来说,CORS通信与同源的AJAX通信没有差别,代码完全一样。浏览器一旦发现AJAX请求跨源,就会自动添加一些附加的头信息,有时还会多出一次附加的请求,用户对这些都不会有感觉。因此,实现CORS通信的关键是服务器。
1,跨域请求分类
a. 请求方式为下列之一:
GET
POST
HEAD
b. 请求头信息不超出以下字段:
Accept
Accept-Language
Content-Language
Content-Type:只限于三个值application/x-www-form-urlencoded、multipart/form-data、text/plain
凡是同时满足以上两个条件的, 就是简单请求.
2,简单请求
对于简单请求, 浏览器直接发出CORS
请求, 即浏览器自动在请求header中加上Origin
字段, 告诉服务器这个请求来自哪个源(请求协议+域名+端口). 服务器收到请求后, 会对比这个字段, 如果字段值不在服务器的许可范围内, 服务器会返回一个正常的HTTP响应, 但是其响应头中不会包含Access-Control-Allow-Origin
字段, 浏览器发现后, 就会抛出一个异常提示响应头中没有这个字段. 如果这个源在服务器的许可范围内, 服务器的响应头会加上以下字段:
Access-Control-Allow-Origin:http://ip:port
必需项, 值为请求头中的Origin的值.Access-Control-Allow-Credentials:true
可选项, 值为boolean, 表示是否允许浏览器发送cookie, 需要在服务器配置.Access-Control-Expose-Headers:
浏览器可以从跨域请求响应头中获取的字段值, 由服务器配置. 默认可以获取Cache-Control、Content-Language、Content-Type、Expires、Last-Modified、Pragma这六个字段.
3,非简单请求
对于非简单请求, 浏览器的CORS请求分为两步, 首先是执行预检(preflight)请求, 询问服务器是否允许当前源访问, 如果允许, 才会执行实际请求, 预检请求可以缓存(缓存时间由服务器定义), 在缓存有效期内再执行CORS请求时无需进行预检请求.
预检请求:
-
a. 预检请求的请求方式为OPTIONs, 表示这个请求是用来询问的。
-
b. 请求头信息包含以下字段:
Origin: 请求源.- Access-Control-Request-Method: cors请求会用到的请求方式.
- Access-Control-Request-Headers: cors请求会额外发送的请求头字段.
-
c. 服务器收到预检请求后会检查上面的三个字段值以确定是否允许跨域请求, 如果任意一项不完全满足则都不允许进行跨域请求.
-
d. 预检请求的响应中会包含如下字段:
- Access-Control-Allow-Origin:
必需项, 值为请求头中的Origin的值. - Access-Control-Allow-Credentials:
可选项, 值为boolean, 表示是否允许浏览器发送cookie, 需要在服务器配置. - Access-Control-Allow-Headers:
可选项, 允许跨域请求额外发送的header字段, 需要在服务器配置. - Access-Control-Allow-Methods:
必需项, 允许跨域请求的请求方式. - Access-Control-Max-Age:
必需项, 预检请求的缓存时间.
- Access-Control-Allow-Origin:
3,解决跨域的几种方法
1,JSONP
JSONP优点是简单兼容性好,可用于解决主流浏览器的跨域数据访问的问题。缺点是仅支持 get 方法具有局限性,不安全可能会遭受 XSS 攻击。
原理就是使用 Script 标签不受同源策略的限制。
用不着,不研究。
2,Spring 的 @CrossOrigin
spring 框架提供 @CrossOrigin,可以用在方法上或者类上,默认允许所有请求和方法。
3,Spring 全局 CORS 配置
Spring:
@Configuration
@EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**");
}
}
SpringBoot
@Configuration
public class MyConfiguration {
@Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurerAdapter() {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**");
}
};
}
}
可以修改任何属性:
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/api/**")
.allowedOrigins("http://domain2.com")
.allowedMethods("PUT", "DELETE")
.allowedHeaders("header1", "header2", "header3")
.exposedHeaders("header1", "header2")
.allowCredentials(false).maxAge(3600);
}
4,基于过滤器的 CorsFilter
作为上述其他方法的替代,Spring框架还提供了CorsFilter。在这种情况下,不用使用@CrossOrigin或WebMvcConfigurer#addCorsMappings(CorsRegistry)
@Bean
public FilterRegistrationBean corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true);
config.addAllowedOrigin("*");
config.addAllowedHeader("*");
config.addAllowedMethod("*");
source.registerCorsConfiguration("/**", config);
FilterRegistrationBean bean = new FilterRegistrationBean(new CorsFilter(source));
bean.setOrder(0);
return bean;
}
5,Nginx 反向代理
原理也是 Cors。
server {
listen 443 ssl;
server_name 这里是域名;
#定义跨域变量
set $cors_origin "";
#访问的域名与填写的域名比较
if ($http_origin ~* "^这里填写调用我们系统的域名地址$") {
set $cors_origin $http_origin;
}
//这里填*就是任何域名都允许跨域
add_header Access-Control-Allow-Origin $cors_origin;
#CORS请求默认不发送Cookie和HTTP认证信息。但是如果要把Cookie发到服务器,要服务器同意,指定
#Access-Control-Allow-Credentials字段。
add_header Access-Control-Allow-Credentials 'true';
#设置跨域请求允许的Header头信息字段,以逗号分隔的字符串
add_header Access-Control-Allow-Headers 'Origin,X-Requested-With,Content-Type,Accept,Authorization,token';
#设置跨域允许的请求
add_header Access-Control-Allow-Metthods 'POST,GET,PUT,OPTIONS,DELETE';
# 预检请求处理
if ($request_method = OPTIONS) {
return 204;
}
"预检"请求用的请求方法是OPTIONS,表示这个请求是用来询问的,因此我们需要在Nginx配置中,针对预检请求进行处理,直接返回204 & Response Header,表示服务器支持允许跨源的访问。
4,CORS 不起作用原因排查
-
是springMVC的版本要在4.2或以上版本才支持@CrossOrigin
-
非@CrossOrigin没有解决跨域请求问题,而是不正确的请求导致无法得到预期的响应,导致浏览器端提示跨域问题。
-
在Controller注解上方添加@CrossOrigin注解后,仍然出现跨域问题,解决方案之一就是
-
对于非简单请求,Option 请求并不会携带所有请求头,如果后端有拦截器之类的验证,而没有跳过 OPTION 请求,导致验证失败返回,此时没有经过 @CrosOrigin 等,浏览器也会体现为跨域问题。
5,参考文献
Spring Cors:https://blog.csdn.net/qq_18671415/article/details/109275495
Cors:https://www.jianshu.com/p/f5a88983f42d