在JBoss EAP 7.1的Java EE应用中运行时动态提供数据源账号密码
针对你在JBoss EAP 7.1上的Java EE 8应用,要移除数据源硬编码凭证并从Vault加载的需求,我有几个经过验证的方案,适配不同的持久化单元类型:
方案一:JTA数据源(容器管理的pu持久化单元)
因为JTA数据源由JBoss容器直接管理,我们需要通过JBoss的JMX API动态修改其安全属性,步骤如下:
1. 修改standalone.xml配置
保留数据源定义,但清空安全凭证:
<datasource jta="true" jndi-name="java:jboss/datasources/pu" pool-name="pupool" enabled="true" use-java-context="true" use-ccm="false"> <connection-url>{TheConnectionUrl}</connection-url> <driver-class>org.postgresql.Driver</driver-class> <driver>postgresql</driver> <!-- 其他池配置、验证配置保持不变 --> <security> <user-name></user-name> <password></password> </security> </datasource>
2. 编写启动时初始化的Singleton EJB
创建一个@Startup @Singleton的EJB,在应用启动时从Vault获取凭证,通过JMX更新数据源配置并重新激活:
import javax.annotation.PostConstruct; import javax.ejb.Singleton; import javax.ejb.Startup; import javax.management.MBeanServer; import javax.management.ObjectName; import java.lang.management.ManagementFactory; @Startup @Singleton public class DatasourceVaultInitializer { @PostConstruct public void setupDatasourceCredentials() { // 1. 从你的Vault实现获取凭证 String dbUsername = fetchUsernameFromVault(); String dbPassword = fetchPasswordFromVault(); try { // 2. 获取JBoss数据源的MBean(注意ObjectName要匹配你的数据源配置) MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer(); ObjectName dsMBeanName = new ObjectName( "jboss.as:subsystem=datasources,data-source=pu,pool-name=pupool" ); // 3. 更新用户名和密码 mBeanServer.invoke( dsMBeanName, "setUserName", new Object[]{dbUsername}, new String[]{String.class.getName()} ); mBeanServer.invoke( dsMBeanName, "setPassword", new Object[]{dbPassword}, new String[]{String.class.getName()} ); // 4. 重启数据源(如果初始启动失败的话) if (!Boolean.TRUE.equals(mBeanServer.getAttribute(dsMBeanName, "Enabled"))) { mBeanServer.invoke(dsMBeanName, "start", new Object[]{}, new String[]{}); } } catch (Exception e) { throw new RuntimeException("Failed to initialize datasource credentials from Vault", e); } } // 替换为你的Vault客户端调用逻辑 private String fetchUsernameFromVault() { // 示例:调用Vault API获取用户名 return "your-vault-loaded-username"; } // 替换为你的Vault客户端调用逻辑 private String fetchPasswordFromVault() { // 示例:调用Vault API获取密码 return "your-vault-loaded-password"; } }
注意事项:
- 确保你的应用有足够的JMX权限来修改数据源配置,可能需要在standalone.xml中调整JMX权限设置。
ObjectName的格式要和JBoss的实际MBean名称匹配,可以通过JBoss CLI或JConsole查看数据源的MBean路径。
方案二:RESOURCE_LOCAL持久化单元(container-unmanaged)
对于非容器管理的持久化单元,我们可以在创建EntityManagerFactory时直接传入从Vault获取的凭证:
import javax.persistence.EntityManagerFactory; import javax.persistence.Persistence; import java.util.HashMap; import java.util.Map; public class UnmanagedEntityManagerFactoryProvider { public EntityManagerFactory createEntityManagerFactory() { // 从Vault获取凭证 String dbUsername = fetchUsernameFromVault(); String dbPassword = fetchPasswordFromVault(); // 构建Hibernate属性,覆盖默认配置 Map<String, Object> properties = new HashMap<>(); properties.put("hibernate.connection.username", dbUsername); properties.put("hibernate.connection.password", dbPassword); // 创建EntityManagerFactory时传入自定义属性 return Persistence.createEntityManagerFactory("container-unmanaged", properties); } // 替换为你的Vault客户端调用逻辑 private String fetchUsernameFromVault() { return "your-vault-loaded-username"; } private String fetchPasswordFromVault() { return "your-vault-loaded-password"; } }
你可以在应用的初始化逻辑(比如ServletContextListener)中调用这个方法来创建EntityManagerFactory,确保凭证在使用前已经加载。
为什么之前的尝试没生效?
- 你提到的
hibernate.datasource.username和hibernate.datasource.password属性只对RESOURCE_LOCAL模式生效,JTA数据源由容器管理,Hibernate不会读取这些属性。 - 应用用户登录的LoginModule确实和数据源凭证无关,那是用于认证应用端的用户,不是数据库连接的凭证。
内容的提问来源于stack exchange,提问作者Valentin
相关产品推荐
相关产品推荐

