一尘不染

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

tomcat

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


阅读 353

收藏
2020-06-16

共1个答案

一尘不染

假设您有一个看起来像这样的tomcat / conf / context.xml文件:

<?xml version="1.0" encoding="utf-8"?>
<Context>
    <WatchedResource>WEB-INF/web.xml</WatchedResource>
    <Resource 
            name="jdbc/MyDB" 
            auth="Container" 
            type="javax.sql.DataSource" 
            removeAbandoned="true" 
            removeAbandonedTimeout="15" 
            maxActive="5" 
            maxIdle="5" 
            maxWait="7000" 
            username="${db.mydb.uid}"
            password="${db.mydb.pwd}"
            driverClassName="${db.mydb.driver}"
            url="${db.mydb.url}${db.mydb.dbName}?autoReconnectForPools=true&amp;characterEncoding=UTF-8"
            factory="com.mycompany.util.configuration.CustomDataSourceFactory"
            validationQuery="SELECT '1';"
            testOnBorrow="true"/>
</Context>

在这种情况下,我们要替换的是此资源定义中$ {。*}中的任何内容。但是,对下面的代码稍加修改,就可以在几乎任何条件下执行这些替换。

注意行 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语句可能最终会将敏感信息打印到日志中,因此,完成调试后,您可能希望删除它们。

在一个旁注中,我之所以这样写是因为我发现许多示例对于一个特定主题而言太具体了(例如,利用密码学),并且我想展示如何通用地做到这一点。此外,这个问题的其他一些答案并不能很好地解释自己,因此我不得不做一些挖掘工作,以弄清楚完成这项工作需要做什么。我想与大家分享我的发现。请随时对此发表评论,提出任何问题,或者在发现问题时进行更正,我将确保将修复内容纳入我的回答。

2020-06-16