记一次SpringBoot中跨域的小问题

记一次SpringBoot中跨域的小问题

问题

前阵子,有个学长在跨域的时候遇到一个问题,我们两个人互相讨论了一番,得到了问题的答案。问题如下:

PXeTS.png

如果按照上图的方式配置跨域类,那么就会出现报403的问题,我把我的配置类发给学长后,果然是没问题了,那么看来问题就出在两个配置类里不一样的地方了

PXQZF.png

解决方式

对比两张图中的配置,可以发现出在了allowedOrigin.allowedOriginPatterns这两个方法上,于是乎我到官方文档上查阅了这两个方法的详细介绍,得到了如下内容:

PYGNs.png

PYCa0.png

现在终于恍然大悟了,文档里都介绍的很清楚了。但是知道了解决方式以后,我去看了一下源码,了解一下到底为什么可以这么做.

源码+相关内容

PYKX5.png

config变量是CorsConfiguration的一个实例化对象。那么看来这两个方法的区别要更深一层,同样是点进去看一下,先看第一个。

PYXYw.png

入参是可以为空的字符串列表,判空之后对非空情况进行一个常见的处理,使用的是函数式编程,其中有一个名为trimTrailingSlash的方法,这个看名字应该是和MySQL里的trim差不多的,点进去看一下

PnLBo.png

果不其然,作用是去掉传入URL最后的一个/。到这里这个方法就全部结束了,看着也太简单了,果然是被抛弃了的一个方法

PnOey.png

上图是第二个方法的内容,同样入参一个可以为空的字符串列表,关键在于这个addAllowedOriginPattern方法,继续点进去看

Pn4tU.png

入参为字符串类型的originPattern,这里就体现出来一个setAllowedOriginPatterns的好处了,如果我们传入了多个需要跨域的格式,那么MVC会帮我们挨个进行处理,调用trimTrailingSlash方法进行/的剔除。看到这里,你可能会有些疑惑,这样看来两个方法不是没什么区别吗?是的,如果看到这里,那确实没啥区别,但是真正的区别其实是下面这个:

PnSHN.png

allowedOrigins是给setAllowedOrigin用的,可以看到列表中的元素类型为String类,那么下面这个allowedOriginPatterns是给setAllowedOriginPatterns用的,列表内容是OriginPattern类,这个类从来没听过,而且一看名字就知道是Spring自己写的,所以我们继续点进去看。

PnH5C.png

类里有一个字符串,两个正则,那肯定两个正则是处理这个字符串的。构造方法OriginPattern,初始化正则格式。这个正则还是很好看懂的,\E和\Q转义出来其实就是要匹配*这个字符,这也就是为什么setAllowedOriginPatterns在官方文档中介绍说可以在传入跨域格式的字符串中填写*号,而不用是已经完全确定的一个格式。


关于CORS中Access-Control-Allow-Origin这个header,规定我们只能设置为如下三种情况:

  • 单域名
  • none

同时如果我们设置为了*,那么Access-Control-Allow-Credentials就不能设置为true了。跨源资源共享(CORS) - HTTP | MDN (mozilla.org)

基于以上的规则,我们总结一下:如果我们需要发送Cookie,那么前端需要设置withCredentials为true,且同时后端也要设置Access-Control-Allow-Credentials为true,并且Access-Control-Allow-Credentials不能设置为*,只能为单域名

但是这时候就又出现一个问题,我的跨域配置类中,withCredentials传参是*啊,而且也不是单域名,为什么跨域还是成功了呢?学了这么长时间的Spring,我多少有个猜测——肯定也是有个很牛逼的东西帮我处理了这个问题

    @Nullable
    public String checkOrigin(@Nullable String origin) {
        if (!StringUtils.hasText(origin)) {
            return null;
        } else {
            String originToCheck = this.trimTrailingSlash(origin);
            Iterator var3;
            if (!ObjectUtils.isEmpty(this.allowedOrigins)) {
                if (this.allowedOrigins.contains("*")) {
                    this.validateAllowCredentials();
                    return "*";
                }

                var3 = this.allowedOrigins.iterator();

                while(var3.hasNext()) {
                    String allowedOrigin = (String)var3.next();
                    if (originToCheck.equalsIgnoreCase(allowedOrigin)) {
                        return origin;
                    }
                }
            }

            if (!ObjectUtils.isEmpty(this.allowedOriginPatterns)) {
                var3 = this.allowedOriginPatterns.iterator();

                while(var3.hasNext()) {
                    OriginPattern p = (OriginPattern)var3.next();
                    if (p.getDeclaredPattern().equals("*") || p.getPattern().matcher(originToCheck).matches()) {
                        return origin;
                    }
                }
            }

            return null;
        }
    }

果不其然,我找到了这个名为checkOrigin的方法,内容中明确写道。如果我们同时将credentials设置为true且allowedPatterns为*,那么这个方法就会把Access-Control=Allow-Origin设置为当前请求过来的origin,也就解决了这个问题。

posted @ 2022-10-05 16:53  Appletree24  阅读(470)  评论(0编辑  收藏  举报