在spring的applicationContext.xml中使用编码密码作为数据源

24

我希望在下面提到的springApplicationContext.xml中保留编码后的密码。

有没有办法实现这个目标?

目前,我已经使用property-placeholder配置了所有属性,就像下面展示的那样,但是我的database.properties文件中仍然包含明文密码。

springApplicationContext.xml

<beans:bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
        <beans:property name="driverClassName"><beans:value>${db.driverClassName}</beans:value></beans:property>
        <beans:property name="url"><beans:value>${db.url}</beans:value></beans:property>
        <beans:property name="username"><beans:value>${db.username}</beans:value></beans:property>
        <beans:property name="password"><beans:value>${db.password}</beans:value></beans:property>
</beans:bean>

但是实际的值存在于我的database.properties中。

db.driverClassName=com.mysql.jdbc.Driver
db.url=jdbc:mysql://localhost/myDB
db.username=root
db.password=root

我希望得到以下内容:

springApplicationContext.xml(与上面相同)

<beans:bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
        <beans:property name="driverClassName"><beans:value>${db.driverClassName}</beans:value></beans:property>
        <beans:property name="url"><beans:value>${db.url}</beans:value></beans:property>
        <beans:property name="username"><beans:value>${db.username}</beans:value></beans:property>
        <beans:property name="password"><beans:value>${db.password}</beans:value></beans:property>
</beans:bean>

但是我的 database.properties 文件中的密码属性值应该是加密格式的。

db.driverClassName=com.mysql.jdbc.Driver
db.url=jdbc:mysql://localhost/myDB
db.username=root
db.password=3g6n72ef8x (using any encription method).

我的数据源在建立新的数据库连接之前会内部解密密码。

非常感谢任何帮助/建议。


当您可以直接获取编码字符串并解码时,您下面建议的方法很有效。但对于真正的加密,您不能仅仅获取加密字符串并解密它:您需要获取明文密码,将其加密,并将其与已加密的值进行比较。例如,请参见org.springframework.security.crypto.password.StandardPasswordEncoder,这是一种类似于您在超越base64编码器后可能使用的东西。因此问题是:您的DataSource如何获取实际密码以检查其是否与db.password的加密值匹配? - Jason
实际上,要求不是在我的database.properties文件中保留原始密码。因此,我的CustomDataSource正在解码database.properties文件的编码值,然后使用它来创建新的数据库连接。 - Sandy
2
我认为,Base64编码的密码本质上与原始密码(即不安全)是相同的,但我猜这个要求的语义是由制定要求的人来决定的。 - Jason
相关链接:https://stackoverflow.com/questions/27567139/spring-jdbc-template-how-to-decrypt-password - Rohim Chou
5个回答

20

可能有些滑稽,我在回答自己的问题。但是我只是想告诉我的解决方案,以及可能遇到相同问题的其他人。

为了简单起见,我使用了BASE64Encoder和BASE64Decoder。稍后我会修改我的代码,使用更安全/更好的加密/解密算法。

我已经使用以下代码对我的数据库密码(例如:我的root)进行了编码:

private String encode(String str) {
        BASE64Encoder encoder = new BASE64Encoder();
        str = new String(encoder.encodeBuffer(str.getBytes()));
        return str;
    }

我将编码后的密码放置在我的 database.properties 文件中,如下所示:

之前的内容

db.driverClassName=com.mysql.jdbc.Driver
db.url=jdbc:mysql://localhost/myDB
db.username=root
db.password=root

之后

db.driverClassName=com.mysql.jdbc.Driver
db.url=jdbc:mysql://localhost/myDB
db.username=root
db.password=cm9vdA==  (Note: encoded 'root' by using BASE64Encoder)

我已经为org.apache.commons.dbcp.BasicDataSource编写了一个包装类,并覆盖了setPassword()方法:

import java.io.IOException;
import org.apache.commons.dbcp.BasicDataSource;
import sun.misc.BASE64Decoder;

public class MyCustomBasicDataSource extends BasicDataSource{

    public CustomBasicDataSource() {
        super();
    }

    public synchronized void setPassword(String encodedPassword){
        this.password = decode(encodedPassword);
    }

    private String decode(String password) {
        BASE64Decoder decoder = new BASE64Decoder();
        String decodedPassword = null;
        try {
            decodedPassword = new String(decoder.decodeBuffer(password));
        } catch (IOException e) {
            e.printStackTrace();
        }       
        return decodedPassword;
    }
}

我正在解码(database.properties)中提供的已编码密码,使用 BASE64Decoder。

并修改了在springApplicationContext.xml文件中提及的dataSource bean的class属性。

<beans:bean id="dataSource" class="edu.config.db.datasource.custom.MyCustomBasicDataSource" destroy-method="close">
    <beans:property name="driverClassName"><beans:value>${db.driverClassName}</beans:value></beans:property>
    <beans:property name="url"><beans:value>${db.url}</beans:value></beans:property>
    <beans:property name="username"><beans:value>${db.username}</beans:value></beans:property>
    <beans:property name="password"><beans:value>${db.password}</beans:value></beans:property>

谢谢。


你难道不需要执行super.password = decode(encodedPassword); 而不是 this.password = decode(encodedPassword); 吗? - Ahmad
9
Base64不是加密,而是编码!使用Base64算法对用户名和密码进行编码通常会使它们无法被肉眼阅读,但编码和解码同样容易。编码步骤并非用于提供安全性,而是将可能存在于用户名或密码中的非HTTP兼容字符编码为HTTP兼容字符。 - Omar Elfada
9
这个解决方案对你的属性文件中的密码进行了base64编码,但是它并没有被加密!我复制粘贴了“cm9vdA==”到https://www.base64decode.org中,立刻得到了“root”。为了其他阅读此问题和答案的人,请修改问题,改为询问“如何使用base64编码的密码”,而不是“如何使用加密密码”,因为这个答案显然没有对其进行加密。我不希望其他读者将编码与加密视为相同的概念。 - Jason

10

创建继承Spring PropertyPlaceHolderConfigurer的自定义PropertyPlaceHolderConfigurer

public class PropertyPlaceholderConfigurer extends
        org.springframework.beans.factory.config.PropertyPlaceholderConfigurer {

    @Override
    protected String convertPropertyValue(final String originalValue) {
        if (originalValue.startwith("SomeText:")) {
            //Apply the decryption logic
            ...
        }
    }
}

您可以加密属性并附加 SomeText:。使用此自定义的PropertyPlaceHolderConfigurer加载属性。


4
我希望您能看到更大的问题:为什么要在属性文件中加密值?在哪种情况下,未经授权的人可以访问您的属性文件?解决存储生产凭据的问题的一种常见技术是将凭据作为环境的一部分而不是源代码的一部分。以下是一些方法:
  • 将属性文件(带有明文密码)放置在生产 Web 服务器的类路径上,这样访问该密码是由对生产机器的访问所控制的。
  • 将属性存储在 web.xml 文件中(context-param with param-name),同样,此文件是您运行代码的环境的一部分,而不是您的代码分发的一部分-访问该文件受到对计算机的访问所控制。
  • 使用 JNDI 并在应用程序服务器中配置该资源。

1
我有一个类似的情况,我们希望在属性文件中使用加密密码,因为我们的应用程序的一部分与另一个应用程序一起安装在客户端服务器上。该代理程序使用另一个应用程序的数据库。我们不希望我们的客户能够访问那个应用程序的数据库。因此,在属性文件中使用加密密码是必要的情景。 - Brett Slocum
这是一个我没有考虑过的有趣情景,谢谢。 - Jason
它仍然只是给出了安全的外观和温暖的感觉,因为任何加密的密码最终都可以被决心的人解密。但它将阻止随意的攻击者,这可能已经足够了。 - Brett Slocum
还有另一种情况需要考虑,即应用程序是独立的。我曾在一家公司工作过,那里有一个与上述情况完全相同的软件,但它是一个独立的桌面应用程序=) - Gabriel Câmara

3
创建一个包装类,实现 Datasource 接口并将其方法调用委托给底层数据源,但在执行前解密密码。

感谢Abhinav的建议,现在我可以使用CustomBasicDataSource来实现这个功能。 - Sandy

0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接