12

有一种方法可以在 xml spring 配置文件中隐藏/加密密码吗?我读到使用 DataSource 的“自定义”子类可以做到这一点,但是解决方案将密钥作为纯文本保存在相同的配置文件中......所以有点没用。

有办法为此使用 KeyStore 吗?例如从密钥库中读取值。

谢谢大家。

4

5 回答 5

12

隐藏密码的目的是什么?我建议您在容器中配置数据源(Tomcat、JBoss 或任何您使用的)并使用 jndi 将数据源注入您的应用程序:

<jee:jndi-lookup id="thedatasource"
                     jndi-name="java:comp/env/jdbc/thedatasource"
                     lookup-on-startup="false"
                     expected-type="javax.sql.DataSource"/>

这样您就不必在应用程序中公开和密码,而只需在 servlet 容器中。

于 2010-11-16T16:44:21.350 回答
11

是的,你可以这么做。您必须围绕数据源类创建一个包装器 bean。这是我以前如何做的一个例子。希望这可以帮助!

<beans>
    <bean id="someDao" class="com.dao.SomeDAOImpl">
         <property name="datasource">
            <ref local="secureDataSource"/>
        </property>
    </bean>
    <bean id="secureDataSource" class="com.ds.SecureDataSource">
        <property name="driverClassName">
            <value><your driver></value>
        </property>
        <property name="url">
            <value><your url></value>
        </property>  
        <property name="username">
            <value><your user id></value>
        </property>
        <property name="password">
            <value><encrypted_pwd></value>
        </property> 
    </bean> 
</beans>

然后在 SecureDataSource 类中,您将需要解密密码。

import java.sql.Connection;
import java.sql.SQLException;


public class SecureDataSource extends DriverManagerDataSource{

    private String url;
    private String username;
    private String password;
    /**
     * @param url the url to set
     */
    public void setUrl(String url) {
        this.url = url;
    }

    /**
     * @param username the username to set
     */
    public void setUsername(String username) {
        this.username = username;
    }

    /**
     * @param password the password to set
     */
    public void setPassword(String password) {
        this.password = password;
    }

    protected Connection getConnectionFromDriverManager() throws SQLException {
        String decryptedPassword = null;
        //decrypt the password here
        return getConnectionFromDriverManager(url,username,decryptedPassword);
    }
}
于 2010-11-16T17:07:15.990 回答
3

已经给出了很好的选择,另一个明显的答案是使用PropertyPlaceholderConfigurer

<context:property-placeholder
    system-properties-mode="OVERRIDE" 
    location="classpath:database.properties" />

<bean id="dataSource" class="com.whatever.datasource.you.Use">
    <property name="password" value="${database.password}" />
</bean> 

现在,您可以将密码保存为属性文件中的属性(如果您不想将其保存在 SCM 中,则可以在部署期间创建)或系统属性(希望其他开发人员也无法使用该属性) )。

澄清: 在部署期间创建有些模糊。我猜你将不得不编写一个安装程序,在最终用户的机器上动态生成属性文件,可能还要加上注册/登录机制。


编辑:我还没有弄清楚你在向谁隐藏信息。两种理论:

a) 有权访问您的源代码的人
b) 您的客户

如果是a),那就走我的路。其他开发人员只需使用调试器启动您的应用程序(突然他进入数据源对象并看到密码),就可以轻松破坏所有其他方式。

如果是 b),那么你基本上没有机会。客户有很多方法可以获取您的密码:调试器、代理、字节码操作、加载时间编织等。即使他不这样做,他也只需附加一个端口嗅探器即可清楚地获取密码文本。唯一安全的做法是每个客户都有一个用户名/密码(永远不要在客户的机器上存储全局密码)。

于 2010-11-16T17:15:51.770 回答
0

我最近有同样的问题。我想将密码的散列版本存储在 .properties 文件中。由于前面的选项,我做到了这一点:我扩展DelegatingDataSource并覆盖了getConnection([...])方法。

public class UnhashingDataSource extends DelegatingDataSource {

    private static final Logger LOGGER = Logger.getLogger(UnhashingDataSource.class);
    private static final int HEX_RADIX = 16;
    private static final String DB_PASS = "a_sample_password";

    @Override
    public Connection getConnection() throws SQLException {
        DriverManagerDataSource dataSource = (DriverManagerDataSource) getTargetDataSource();
        return getConnection(dataSource.getUsername(), dataSource.getPassword());
    }

    @Override
    public Connection getConnection(String username, String password) throws SQLException {
        try {
            DataSource datasource = getTargetDataSource();
            if (datasource == null) {
                throw new RuntimeException("targetDataSource is null");
            }
            MessageDigest md = MessageDigest.getInstance("SHA-1");
            md.reset();
            md.update(DB_PASS.getBytes());
            if (password.equals(getHexString(md.digest()))) {
                return datasource.getConnection(username, DB_PASS);
            } else {
                throw new RuntimeException("Unable to connect to DB");
            }
        } catch (NoSuchAlgorithmException e) {
            LOGGER.error("Unknown algorithm");
        }
        return null;
    }

    private String getHexString(final byte[] messageDigest) {
        BigInteger bigInt = new BigInteger(1, messageDigest);
        return bigInt.toString(HEX_RADIX);
    }
}

然后,这是我在我的applicationContext.xml

# Using the unhashing datasource
<bean id="entityManagerFactory"
    class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
    <property name="dataSource" ref="unhashingDataSource" />
    # ...
</bean>
<bean id="hashedDataSource"
    class="org.springframework.jdbc.datasource.DriverManagerDataSource">
    <property name="driverClassName" value="${datasource.driverClassName}" />
    <property name="url" value="${datasource.url}" />
    <property name="username" value="${datasource.username}" />
    <property name="password" value="${datasource.hash}" />
</bean>
<bean id="unhashingDataSource"
    class="my.package.UnhashingDataSource">
    <property name="targetDataSource" ref="hashedDataSource" />
</bean>

datasource.hash存储的属性(来自 .properties 文件)在哪里:

datasource.hash = 2e54b0667ef542e3398c55a08a4e04e69b9769e8

普通密码仍然在字节码中,但不再直接在 .properties 文件中。

于 2012-10-19T09:33:29.807 回答
0

感谢您的所有帖子和查询。

希望访问者通过阅读此页面了解加密密码的技术方法。我想在这里补充一件重要的事情,如果您正在处理生产,那么肯定会建议您使用任何“安全哈希算法”,例如 SHA-256 和盐。您可以考虑使用盐作为行业标准的安全哈希算法。

于 2016-02-22T20:05:58.500 回答