如何将值动态加载到Tomcat的Context XML文件中

鉴于Tomcat的Context XML文件往往包含敏感信息(通常包括连接到数据库所需的凭据),如何从纯文本context.xml以外的源动态加载这些值?

假设您有一个tomcat / conf / context.xml文件,如下所示:

  WEB-INF/web.xml   

在这种情况下我们想要替换的是此资源定义中$ {。*}内容中的任何内容。 但是,稍微修改下面的代码,您可以在几乎任何您想要的标准上执行这些替换。

注意line factory="com.mycompany.util.configuration.CustomDataSourceFactory"

这意味着Tomcat将尝试使用此工厂来处理此资源。 应该提到的是,这意味着这个工厂必须在启动时在Tomcat的类路径上(就我个人而言,我将它放在Tomcat lib目录中的JAR中)。

这是我工厂的样子:

 package com.mycompany.util.configuration; import java.util.Hashtable; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.naming.Context; import javax.naming.Name; import javax.naming.RefAddr; import javax.naming.Reference; import javax.naming.StringRefAddr; import javax.naming.spi.ObjectFactory; import org.apache.commons.dbcp.BasicDataSourceFactory; public class CustomDataSourceFactory extends BasicDataSourceFactory implements ObjectFactory { private static final Pattern _propRefPattern = Pattern.compile("\\$\\{.*?\\}"); //http://tomcat.apache.org/tomcat-6.0-doc/jndi-resources-howto.html#Adding_Custom_Resource_Factories @Override public Object getObjectInstance(Object obj, Name name, Context nameCtx, Hashtable environment) throws Exception { if (obj instanceof Reference) { Reference ref = (Reference) obj; System.out.println("Resolving context reference values dynamically"); for(int i = 0; i < ref.size(); i++) { RefAddr addr = ref.get(i); String tag = addr.getType(); String value = (String) addr.getContent(); Matcher matcher = _propRefPattern.matcher(value); if (matcher.find()) { String resolvedValue = resolve(value); System.out.println("Resolved " + value + " to " + resolvedValue); ref.remove(i); ref.add(i, new StringRefAddr(tag, resolvedValue)); } } } // Return the customized instance return super.getObjectInstance(obj, name, nameCtx, environment); } private String resolve(String value) { //Given the placeholder, do stuff to figure out what it's true value should be, and return that String. //This could be decryption, or maybe using a properties file. } } 

然后,一旦此代码在类路径上,重新启动Tomcat并观察catalina.out以获取日志消息。 注意: System.out.println语句可能最终会将敏感信息打印到您的日志中,因此您可能希望在完成调试后删除它们。

在旁注中,我写了这个,因为我发现许多例子对于一个特定主题(例如利用密码学)来说太具体了,我想说明如何一般地完成这个。 此外,这个问题的其他一些答案并没有很好地解释自己,我不得不做一些挖掘来弄清楚需要做些什么来使这项工作。 我想和你们分享我的发现。 如果您发现问题,请随时对此发表评论,提出任何问题或进行更正,我将确保将修复内容添加到我的答案中。

如果你想这样做,你可以实现自己的类,实现非常简单

org.apache.tomcat.util.IntrospectionUtils.PropertySource

接口并使用系统属性注册它

org.apache.tomcat.util.digester.PROPERTY_SOURCE

我们也是这样做的,因为它能够在context.xml,server.xml等中使用加密值。

请参见http://tomcat.apache.org/tomcat-7.0-doc/config/systemprops.html#Property_replacements

如果您正在实现zookeeper属性源,我对结果感兴趣,因为我正在考虑做同样的事情。

为什么要经历所有麻烦?

只需将主机上的敏感参数配置为JVM系统属性 ,Tomcat就会自动识别它们并替换所有占位符${...} 。 这样,敏感数据只保留在主机上 ,并且永远不会泄露在源代码中。

来自: https : //tomcat.apache.org/tomcat-7.0-doc/config/

支持Apache Ant样式变量替换; 可以使用语法$ {propname}在配置文件中使用名为propname的系统属性。 所有系统属性都可用,包括使用-D语法设置的属性,JVM自动提供的属性以及$ CATALINA_BASE / conf / catalina.properties文件中配置的属性。

@注意:这个答案假定您的context.xml和其他Tomcat配置文件在SCM下,这是使用虚拟化部署时通常会发生的情况(例如Openshift)。

你不能。 这个问题意味着无限回归。 如果您有安全来源加载凭据,则需要为安全源定义凭据,依此类推+ ad infinitum。+

最终,这类事情的答案是服务器机器的物理安全性,以及对谁可以从外部查看其部署目录的访问控制。