一直在小程序环境下开发,跨域问题一直不明显。
忽一日,需要做一个简单的web,访问小程序服务端一个简单的接口。
需求很简单,实现很容易,噼里啪啦敲好上测试服务器——糟糕,访问不了!
前端一看,明显是跨域问题嘛,那就加了一个请求头:
$.ajax({
type: 'GET',
headers: {
"Access-Control-Allow-Origin":"http://example.edu",
"Access-Control-Allow-Headers":"X-Requested-With",
'Access-Control-Allow-Origin': '*'
},
url: 'xxx.xxx.com'
dataType: 'json',
success: function(res){
console.log(res);
}
});
再访问,还不行,于是又加上了同源策略:
<meta name="referrer" content="always">
结果访问直接报:unsafe-url
于是呼叫服务端,服务端一看,跨域问题,那就配置服务端允许跨域,直接在中转服务中加上CorsConfig:
@Configuration
public class CorsConfig {
@Bean
public CorsWebFilter corsFilter() {
CorsConfiguration config = new CorsConfiguration();
config.addAllowedMethod("*");
config.addAllowedOrigin("*");
config.addAllowedHeader("*");
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(new PathPatternParser());
source.registerCorsConfiguration("/**", config);
return new CorsWebFilter(source);
}
}
再试,还是不行!
那就再加,在业务实现服务中也加上CorsConfig
再试,还是不行!
那就在网关中再加!
这时,服务端人员才醒悟过来,这特么不是跨域不够,是跨域过多,多次跨域导致业务根本无法响应出来!
于是全业务代码搜索跨域代码,先去掉业务实现服务上的跨域,再去掉中转服务上的跨域,再去掉网关配置中的跨域,再试!
从这个响应头上看,应该还有一个地方设置跨域重复了。
而目前整个业务代码里面只保留了一个全局的CorsFilter和若干个@CrossOrigin,而我们要访问到的业务上可以确定没有@CrossOrigin注解的!
这就奇怪了!
这个时候后端人员才悠悠的想起:年初的某一天,网关上面加了一个CorsConfig,当时在测试上没有测试通过,于是代码就没有提交,而测试服上运行的网关服务可能CorsConfig还没有删!
众人醒悟,赶紧重新打包网关服务替换上去!
再一试,还是不行!
再编辑前端页面,把referrer的meta和请求头中的header全部去掉,终于可以了!
综上,就是这次多层跨域问题引出来的问题解决过程。
代码中还存在着很多的@CrossOrigin注解的跨域设置,翻看其DefaultCorsProcessor.processRequest的实现,大致如下:
public boolean processRequest(@Nullable CorsConfiguration config, HttpServletRequest request, HttpServletResponse response) throws IOException {
Collection<String> varyHeaders = response.getHeaders("Vary");
if (!varyHeaders.contains("Origin")) {
response.addHeader("Vary", "Origin");
}
if (!varyHeaders.contains("Access-Control-Request-Method")) {
response.addHeader("Vary", "Access-Control-Request-Method");
}
if (!varyHeaders.contains("Access-Control-Request-Headers")) {
response.addHeader("Vary", "Access-Control-Request-Headers");
}
if (!CorsUtils.isCorsRequest(request)) {
return true;
} else if (response.getHeader("Access-Control-Allow-Origin") != null) {
logger.trace("Skip: response already contains \"Access-Control-Allow-Origin\"");
return true;
} else {
boolean preFlightRequest = CorsUtils.isPreFlightRequest(request);
if (config == null) {
if (preFlightRequest) {
this.rejectRequest(new ServletServerHttpResponse(response));
return false;
} else {
return true;
}
} else {
return this.handleInternal(new ServletServerHttpRequest(request), new ServletServerHttpResponse(response), config, preFlightRequest);
}
}
}
可见其源码中对请求头是否设置了Origin、Access-Control-Request-Method、Access-Control-Request-Headers有过判断,没有设置才会设置响应头信息,所以一般多层使用@CrossOrigin注解不会出问题。
但应用中已经存在全局的自定义跨域配置了,就统一使用全局自定义配置吧,不要再自行配置@CrossOrigin了,以免哪一天又出现莫名的问题!