1. 程式人生 > >Jboss 資料來源密碼明文加密 + 小程式

Jboss 資料來源密碼明文加密 + 小程式

現在一個客戶做審計要求需要把儲存在檔案中的資料庫密碼加密,網上也有很多教程了,這裡主要是做一個補充,寫了一個小程式,批量修改配置,為什麼這樣做呢?

我們這邊的產品在Jboss中配置了多個數據源,數一下:1,2,3...大概有10個吧,可能更多,一個個改費時呀.

這邊的Jboss版本為 5.1.0.

下面來看一看.

預設資料來源配置如下:

<local-tx-datasource>
	<jndi-name>jdbc/ADMIN</jndi-name>
	<connection-url>jdbc:oracle:thin:@sce10:1521:XXXX</connection-url>
	<driver-class>oracle.jdbc.OracleDriver</driver-class>
	<user-name>XXXX</user-name>
	<password>XXXX</password>
	<min-pool-size>0</min-pool-size>
	<max-pool-size>100</max-pool-size>
	<new-connection-sql>ALTER SESSION SET NLS_DATE_FORMAT = 'yyyy/mm/dd hh24:mi:ss'</new-connection-sql>
</local-tx-datasource>
使用者名稱密碼都是明文.

我們可以進行小小的更改,編輯它:

<local-tx-datasource> 
    <jndi-name>jdbc/ADMIN</jndi-name>  
    <connection-url>jdbc:oracle:thin:@sce10:1521:XXXX</connection-url>  
    <driver-class>oracle.jdbc.OracleDriver</driver-class>  
    <min-pool-size>0</min-pool-size>  
    <max-pool-size>100</max-pool-size>  
    <new-connection-sql>ALTER SESSION SET NLS_DATE_FORMAT = 'yyyy/mm/dd hh24:mi:ss'</new-connection-sql>  
    <security-domain>SCPRD_Encrypt_ADMIN_Password</security-domain>
</local-tx-datasource>  
編輯後,變成如上所示,那麼這個安全域配置在哪裡呢,往下看

編輯 JBOSS_HOME/server/Default/Conf/login-config.xml,新增一個節點:

<application-policy name="SCPRD_Encrypt_SCPRD_WMWHSE1_Password">
	<authentication>
		<login-module code="org.jboss.resource.security.SecureIdentityLoginModule" flag="required">
			<module-option name="username">wmwhse1</module-option>
			<module-option name="password">-7777fac1e4b05d45207a6df87216de44</module-option>
			<module-option name="managedConnectionFactoryName">jboss.jca:service=LocalTxCM,name=jdbc/ADMIN</module-option>
		</login-module>
	</authentication>
</application-policy>
name: DataSource中 security-domain 對應,一定要一致.

username:資料庫使用者名稱

password:加密後的密碼

managedConnectionFactoryName:name={Datasource 中的JndiName},這點也很重要

其實上面都挺簡單的.

下面重點來了,密碼加密命令(其實就是載入Jboss的一個jar檔案,呼叫Java類生成輸出)

java -cp "C:\Infor\sce\jboss-5.1.0.GA\lib\jboss-logging-spi.jar;C:\Infor\sce\jboss-5.1.0.GA\lib\jboss-jmx.jar;C:\Infor\sce\jboss-5.1.0.GA\lib\jboss-common-core.jar;C:\Infor\sce\jboss-5.1.0.GA\common\lib\jboss-jca.jar;C:\Infor\sce\jboss-5.1.0.GA\common\lib\jbosssx.jar" org.jboss.resource.security.SecureIdentityLoginModule $password$

執行就行了,控制檯會輸出密碼的.

重點又來了,資料來源太多,一個個改費勁,上一小段程式碼.

package org.xml.datasource;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.util.List;

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.Node;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.SAXReader;
import org.dom4j.io.XMLWriter;

/**
 * 資料來源批量加密
 * @author berr
 * 2014.11.07
 */
public class Convert2Encoded {

	/**
	 * 載入XML
	 * @param path 檔案路徑
	 * @return 返回Dom4j解析物件
	 */
	public Document load(String path){
		InputStream is = null;
		try {
			is = new FileInputStream(new File(path));
			SAXReader saxr = new SAXReader();
			return  saxr.read(is);
			
		} catch (Exception e) {
			is = null;
			e.printStackTrace();
		}finally{
			try {
				is.close();
			} catch (IOException e) {
				is = null; 
			}
		}
		return null;
	}
	
	/**
	 * 轉換
	 * @param document
	 * @throws Exception
	 */
	public void convertTo(Document document) throws Exception{
		
		List<Element> datasources = document.selectNodes("//datasources/local-tx-datasource"); //XPATH 獲取資料來源節點
		
		for (Element d : datasources) {
			
			String jndiName = d.selectSingleNode("jndi-name").getText();
			
			Node user = d.selectSingleNode("user-name");
			Node pass = d.selectSingleNode("password");
			
			
			String userName = user.getText();
			String password = pass.getText();
			password = encryptPassword(password); //加密密碼,此classpath已加入jboss的幾個jar檔案
			
			//移除使用者名稱密碼節點
			d.remove(user); 
			d.remove(pass);
			
			
			d.addElement("security-domain"); //增加安全域節點
			
			String newName = createName(jndiName);
			
			d.selectSingleNode("security-domain").setText(newName);
			
			output(newName,jndiName,userName,password);
			
		}
		
		
		
	}
	
	/**
	 * 輸出 login-config.xml 配置資訊,需要手工複製新增
	 * @param name
	 * @param jndiName
	 * @param user
	 * @param password
	 */
	public void output(String name,String jndiName,String user,String password){
		StringBuffer tpl = new StringBuffer("<application-policy name=\"%s\">\n");
		tpl.append("\t<authentication>\n");
		tpl.append("\t\t<login-module code=\"org.jboss.resource.security.SecureIdentityLoginModule\" flag=\"required\">\n");
		tpl.append("\t\t\t<module-option name=\"username\">%s</module-option>\n");
		tpl.append("\t\t\t<module-option name=\"password\">%s</module-option>\n");
		tpl.append("\t\t\t<module-option name=\"managedConnectionFactoryName\">jboss.jca:service=LocalTxCM,name=%s</module-option>\n");
		tpl.append("\t\t</login-module>\n");
		tpl.append("\t</authentication>\n");
		tpl.append("</application-policy>");
		System.out.println(String.format(tpl.toString(), name,user,password,jndiName));
	}
	
	
	public void startConvert(String path) throws Exception{
		Document doc = load(path);
		
		convertTo(doc);
		
		System.out.println("---------------------------------------------------------------------------");
		System.out.println("1.The above content append to jboss/conf/login-config.xml ");
		
		String newpath = path + ".new";
		
		System.out.println("---------------------------------------------------------------------------");
		System.out.println("2.The new datasrouce config file ["+newpath+"] replace to jboss/deploy/*.ds.xml ");
		
		writer(newpath,doc);
	}
	
	public String createName(String jndiName){
		
		int i = jndiName.indexOf("/");
		
		String newName = jndiName;

		if(i > -1){
			newName = jndiName.substring(i + 1, jndiName.length());
		}
		
		String tpl = "SCPRD_Encrypt_%s_Password";
		return String.format(tpl, newName);
		
	}
	
	
	/**
	 * 加密密碼
	 * @param password
	 * @return
	 * @throws Exception
	 */
	public String encryptPassword(String password) throws Exception{
		
		/**
		 這裡大家會覺得很奇怪,為什麼要用反射,下面解答一下
		 Jboss這個SecureIdentityLoginModule類是做加密解密的,但是他的加密方法是私有的,我這個包不能訪問
		 你可能會問呼叫main方法也可以呀,回答是NO,main方法是在控制檯輸出,不是返回一個變數
		 那麼問題來了,怎麼辦? 反射就可很簡單的解決了,下面程式碼就是
		 * */
		
		Class clz = org.jboss.resource.security.SecureIdentityLoginModule.class;
		Method m = clz.getDeclaredMethod("encode", String.class); 
		m.setAccessible(true);
		Object o = m.invoke(null, password);
		
		return String.valueOf(o);
		
		
	}
	
	/**
	 * 寫入XML
	 * @param path
	 * @return
	 * @throws DocumentException 
	 */
	public static void writer(String path,Document document) throws DocumentException{

		XMLWriter writer = null;// 宣告寫XML的物件
		try {
			OutputFormat format = OutputFormat.createPrettyPrint();
			writer = new XMLWriter(new FileWriter(path), format);
	        writer.write(document);
	        writer.close();
		} catch (Exception e) {
			e.printStackTrace();
		}finally{
			try {
				writer.close();
			} catch (IOException e) {
				writer = null;
			}
		}
	}
	
	public static void main(String[] args) throws Exception {
		Convert2Encoded c2e = new Convert2Encoded();
		
		c2e.startConvert("D:\\work\\workspace\\RESOURCE\\tools\\source\\scewebserver-ds.xml");
		
		
		
	}
}



到此結束.