Back
Featured image of post 关于CORS的Access-Control-Allow-Credentials设置问题

关于CORS的Access-Control-Allow-Credentials设置问题

前言

众所周知,跨域设置CORS的话我们需要设置Access-Control-Allow-OriginAccess-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则会请求失败,反过来则可以。

Licensed under CC BY-NC-SA 4.0
comments powered by Disqus
一辈子热爱技术
Built with Hugo
Theme Stack designed by Jimmy
gopher