一种深度自定义 Apereo CAS 的思路

搬砖过程中,遇到了一个问题:需要自定义 CAS 的 Audit Log,更改其对 Client IP 的获取逻辑。在 CAS 源代码中查找,发现 Audit Log Entry 来自 ClientInfo 对象。而 ClientInfo 对象则是在 org.apereo.inspektr.common.web.ClientInfoThreadLocalFilter 这个拦截器中收集并放置到 ThreadLocal 变量中去的。

这个拦截器在 cas.war 中的 configuration 类里通过 FilterRegistrationBean 被注册到 Spring 的 Filter Chain 里去。细看这个 bean 的配置,发现并没有 @ConditionalOnMissingBean 注解,导致我们没法在 cas-overlay 里提供一个 bean 去替换掉这个 FilterRegistrationBean。所以在 https://blog.dragonslayer.me/archives/150 这篇文章的最后一段我们可以看到,我用了 BeanFactoryPostProcessor 去完成需求。

那么,这个问题有没有其他的解决方法呢?本文提供了另一种思路。

gradle-war-overlay 的最底部可以看到 exclude 这个命令,它的作用是从 war 包里过滤掉某些文件。如果我们使用这个命令把一些 core 中的 jar 干掉,然后再把自己做过修改的 jar 通过 dependencies block 引入,是不是就能够做到不重新编译 cas.war 而又可以自定义这里面的代码了呢?

事实上,这个操作还能用在别的方面。例如我在引入公司自定义的 log4j2 的依赖时遇到了经典的 multiple binding found 的警告日志,原因是在 cas.war 中已经有了 log4j2 binding 的情况下,我又在外层引入了 log4j2,这样在运行时 classpath 里就有了两个 log4j2 binding. 或者是你想用 logback ,但是 cas.war 里已经有了 log4j2 ,你还是会遇到这个问题。

此时可以这么做:

war {
    dependsOn copyConfig
    baseName 'cas'
    includeWarJars = true
    entryCompression = ZipEntryCompression.STORED

    def excludeList = [
            // avoid multiple log4j bindings warning
            "WEB-INF/lib/log4j-*.jar",
    ]
    exclude(*excludeList)
}

excludeList 中加入多个 jar 的路径来干掉 jar。

类似的,通过 CAS 项目里的包名去确定 jar 包的名字,然后加入到 exclude list 中,再引入修改过的 jar 包就可以完成本文开头提到的需求了。当然本人并没有尝试过这种方法,等需要其他深度定制的需求的时候再试试吧,现在只是提供一种思路而已。


2021.12.14 更新:想不到几个月之前的研究成果现在派上了用场。LOG4J2-3201 这个漏洞导致大量 Java 服务受到影响,CAS 也不例外。我使用的是 CAS 5.3 版本,现在已经不再提供安全更新。要从 cas-overlay 里更新 Log4j2 的版本应该就要使用这个办法。我在 clean build 之后把 war 包解开看过,所有 log4j2 的组件都是我指定的版本,不会存在有漏洞的版本。只要更新到 2.15.0 即可修复这个漏洞。

此条目发表在折腾 CAS分类目录,贴了, , , , 标签。将固定链接加入收藏夹。

发表评论

您的电子邮箱地址不会被公开。 必填项已用*标注

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据