在现在流行的前后端分离开发中,跨域问题突显了出来,跨域问题的根本原因:浏览器有同源策略限制,当前域名的js只能读取同域下的窗口属性,这是一个基础安全功能。那么什么是同源策略呢?即两资源的URL中 协议,域名,端口,都相同则称为同源,若两资源为不同源,则不允许共享资源。
例如:以http://www.baidu.com/dir1/a.html为例子
请求地址 | 结果 | 原因 |
---|---|---|
http://www.baidu.com/dir1/b.html | 成功 | 同一域名端口,相同文件夹 |
http://www.baidu.com/dir2/a.html | 成功 | 同一域名端口,不同文件夹 |
https://www.baidu.com/dir1/b.html | 失败 | 不同协议,http与https |
http://www.baidu.com:8000/dir1/b.html | 失败 | 不同端口,默认为80 |
http://www.sina.com/dir1/b.html | 失败 | 不同域名 |
注: 核心判断方式为,端口及端口左边的地址要完全一致
CORS是Cross-Origin Resource Sharing的缩写。也就是支持跨域资源共享,即不同域之间可以进行资源的相互访问。那么为什么要有CORS呢,其实很简单,浏览器的同源政策保证了安全的同时,也让我们不能访问其它域的资源,即使开发者认为的安全操作也不行,这时,我们就可以用一套协议来告诉浏览器,这某几个域在我们的服务范围之内,不要一棍打死。即有如下改变:
CORS出现之前:
浏览器接收到服务器回复 --> 检查是否同源 --> 不同源就拒绝响应!/同源就响应!
CORS出现之后:
浏览器接收到服务器回复 --> 检查一下response header --> 发现有特定字段Access-Control-Allow-Origin --> 检查这个header后面的值包不包含自己所在的域(打比方我们现在在www.baidu.com),浏览器检查发现后response header里面写的有Access-Control-Allow-Origin: https://www.baidu.com, https://www.google.com, 正好我们在允许的域里面! --> 浏览器响应服务器返回的数据(response)。
CORS需要浏览器和服务器也就是B/S两端分别实现!
接收到服务器的回复之后检测回复头里面是否含有特定字段!有的话查看自己的域名是否在内,一定要这两个条件都符合!浏览器才会接受(或者说响应)服务器返回的回复!
服务器需要在接受到浏览器发来的请求(request)后,在返回response之前!设置header,在header中里面包含特定字段,以便浏览器收到回复之后可以去检查,有字段证明服务器同意服务这个网站,浏览器就可以接受回复,没有字段或者字段的值不包含发送请求的网站,浏览器就不会响应回复内容!
如果是复杂的跨域请求,浏览器则会先发送一个OPSTIONS探路,收到响应允许跨域后会再发送真实请求。
注:此时服务端写代码时,还需响应(OPTIONS)允许跨域。
Nignx和前端的解决方案自行查找下吧,这里介绍下后端的常见解决方案
在controller上加上@CrossOrigin 注解或者@CrossOrigin(origins = “http://localhost:8081”),优点是粒度更细,某URL是否跨域可以分开配置,缺点是,如果整个项目都要跨域,则每个controller都需要添加注解
@RestController
@RequestMapping(path = "/user")
@CrossOrigin // 解决跨域问题
public class UserController {
//......
}
创建配置类CorsConfig,此方法统一配置,用的较多
@Configuration
public class CorsConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*")
.allowedMethods("GET", "HEAD", "POST", "PUT", "DELETE", "OPTIONS")
.allowCredentials(true)
.maxAge(3600)
.allowedHeaders("*");
}
}
创建一个过滤器CorsFilter
@WebFilter(filterName = "CorsFilter ")
@Configuration
public class CorsFilter implements Filter {
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletResponse response = (HttpServletResponse) res;
response.setHeader("Access-Control-Allow-Origin","*");
response.setHeader("Access-Control-Allow-Credentials", "true");
response.setHeader("Access-Control-Allow-Methods", "POST, GET, PATCH, DELETE, PUT");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
HttpServletRequest httpRequest = (HttpServletRequest) req;
// OPTIONS method response
if (httpRequest.getMethod().equals("OPTIONS"))
((HttpServletResponse) res).sendError(HttpServletResponse.SC_OK);
chain.doFilter(req, res);
}
}
网关配置跨域(用于微服务)
@Configuration
public class CorsConfiguration {
@Bean
public CorsWebFilter corsWebFilter(){
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration corsConfiguration = new CorsConfiguration();
//1、配置跨域
corsConfiguration.addAllowedHeader("*");
corsConfiguration.addAllowedMethod("*");
corsConfiguration.addAllowedOrigin("*");
// 是否允许携带cookie跨域
corsConfiguration.setAllowCredentials(true);
source.registerCorsConfiguration("/**",corsConfiguration);
return new CorsWebFilter(source);
}
}
https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Access_control_CORS