前言
众所周知,跨域设置CORS的话我们需要设置Access-Control-Allow-Origin
、Access-Control-Allow-Methods
等header,但是我们的Access-Control-Allow-Origin
只能设置为三种情况:
- 星号 *
- 单域名
- none
同时还有一个限制就是设置为星号的时候,Access-Control-Allow-Credentials
不能设置为true,下面来自MDN:
对于附带身份凭证的请求,服务器不得设置
Access-Control-Allow-Origin
的值为“*
”。这是因为请求的首部中携带了Cookie
信息,如果Access-Control-Allow-Origin
的值为“*
”,请求将会失败。
而Access-Control-Allow-Credentials
则一般是服务器用来设置是否允许前端携带Cookies的标志位,withCredentials
是前端用来表示是否给服务器发请求的时候带上Cookies的标志位:
将
[XMLHttpRequest](https://developer.mozilla.org/en-US/DOM/XMLHttpRequest)
的withCredentials
标志设置为true
,从而向服务器发送 Cookies。因为这是一个简单 GET 请求,所以浏览器不会对其发起“预检请求”。但是,如果服务器端的响应中未携带Access-Control-Allow-Credentials: true
,浏览器将不会把响应内容返回给请求的发送者。
基于以上的规则,如果我们需要发Cookies的话,前端withCredentials
和后端的Access-Control-Allow-Credentials
都要设置为true,同时Access-Control-Allow-Origin
不能设置为星号,只能设置为单域名。
问题
但是在我看一个开源项目源码的时候,看到它CORS的地方:
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**").allowedOrigins("*")
.allowedMethods("GET", "HEAD", "POST", "PUT", "DELETE", "OPTIONS")
.allowCredentials(true).maxAge(3600);
}
出现了疑惑,为什么它同时设为星号同时设置了true但是仍然请求正常呢?
打开了F12看响应的header,发现Access-Control-Allow-Origin
是前端的地址,不是设置了星号吗,怎么变成单origin了?然后把allowCredentials方法里传个false,发现请求就异常了,开始搞不懂了,因为这个项目其实也没有用到cookies呀,只用了token,token也是放在header里的。
探究
然后只能猜测是Spring MVC帮我们做了这一切,就去扒源码了,果然扒到了!
在org.springframework.web.cors
下的CorsConfiguration
类,有这么一个checkOrigin
方法:
@Nullable
public String checkOrigin(@Nullable String requestOrigin) {
if (!StringUtils.hasText(requestOrigin)) {
return null;
} else if (ObjectUtils.isEmpty(this.allowedOrigins)) {
return null;
} else if (this.allowedOrigins.contains("*")) {
return this.allowCredentials != Boolean.TRUE ? "*" : requestOrigin;
} else {
Iterator var2 = this.allowedOrigins.iterator();
String allowedOrigin;
do {
if (!var2.hasNext()) {
return null;
}
allowedOrigin = (String)var2.next();
} while(!requestOrigin.equalsIgnoreCase(allowedOrigin));
return requestOrigin;
}
}
在判断到我们设置类星号且allowCredentials也设置为true后,会把Access-Control-Allow-Origin
设置为当前请求的origin,也就是动态去设置它,这个值就一直会是请求过来的origin,解决了跨域的同时也不会违反星号和true的限制,想想之前确实有这种写法,不过其实我们不用手动去实现这个过程,因为框架都帮我们做好了。
当然,如果项目只用token不用cookies的话,withCredentials和allowCredentials同时设置为false,allowOrigin设置为星号,也是没问题的。
总结
Access-Control-Allow-Origin
设置为星号时,Access-Control-Allow-Credentials
不能同时设置为true,Spring MVC帮我们规避了这个错误,会在这种情况下帮你把allowOrigin设置为当前request的origin。
如果前端withCredentials为true,但是后端的Access-Control-Allow-Credentials
为false则会请求失败,反过来则可以。