几年前研究过Tomcat context.xml 中数据库密码改为密文的内容,因为当时在客户云桌面代码没有留备份也没有文章记录,最近项目又提出了这个需求就又重新拾起来学习一下。在网上找了一些资料,自己也大概试了一下,目前功能是实现了。
参考链接:
https://blog.csdn.net/fzzsh/article/details/8863338
https://blog.csdn.net/T_P_F/article/details/118552417
Tomcat 常见情况
正常的tomcat context.xml配置文件数据库用户名和密码都是明文的
<Resource name="jdbc/DSamdb" auth="Container" type="javax.sql.DataSource" description="test DB"
initialSize="10"
maxWaitMillis="10000" maxTotal="80" maxIdle="20" minIdle="10" validationQuery="select 1"
username="root" password="root" driverClassName="com.mysql.jdbc.Driver"
url="jdbc:mysql://localhost:3306/webtest?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8&useSSL=false"
/>
现在因为安全要求或者国家等保要求,这个数据库密码是需要加密的,并且要定期修改的,首先实现加密功能
Tomcat context.xml密码加密
tomcat加密是通过在配置文件中增加 factory参数让数据源的解析指到定制化的jar包中,对密码或用户名进行解析,加密后的配置文件信息
<Resource name="jdbc/DSamdb" auth="Container" type="javax.sql.DataSource" description="test DB"
initialSize="10"
maxWaitMillis="10000"
maxTotal="80" maxIdle="20" minIdle="10" validationQuery="select 1"
factory="com.axb.data.factory.DataSourceFactory"
username="root"
password="726f6f74"
driverClassName="com.mysql.jdbc.Driver"
url="jdbc:mysql://localhost:3306/webtest?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8&useSSL=false"
/>
factory="com.axb.data.factory.DataSourceFactory" 是包名+类名。password 的root已经加密为“726f6f74”,从网上找到加密算法比较简单,所以加密串看着也挺简单。
配置信息啰嗦完了,上代码:
DataSourceFactory.java 继承BasicDataSourceFactory
package com.axb.data.factory;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Properties;
import javax.naming.Context;
import javax.naming.Name;
import javax.naming.RefAddr;
import javax.naming.Reference;
import javax.naming.StringRefAddr;
import javax.sql.DataSource;
import net.sf.json.JSONObject;
import org.apache.tomcat.dbcp.dbcp.BasicDataSource;
import org.apache.tomcat.dbcp.dbcp.BasicDataSourceFactory;
import com.axb.data.factory.util.Encode;
import com.axb.data.factory.util.HttpUtil;
public class DataSourceFactory extends BasicDataSourceFactory {
@Override
public Object getObjectInstance(Object obj, Name name, Context nameCtx,
Hashtable environment) throws Exception {
if (obj instanceof Reference) {
//用户名没有加密
// setUsername((Reference) obj);
setPassword((Reference) obj);
}
return super.getObjectInstance(obj, name, nameCtx, environment);
}
private void setUsername(Reference ref) throws Exception {
findDecryptAndReplace("username", ref);
}
private void setPassword(Reference ref) throws Exception {
findDecryptAndReplace("password", ref);
}
private void findDecryptAndReplace(String refType, Reference ref)
throws Exception {
int idx = find(refType, ref);
System.out.println(idx +"----->findDecryptAndReplace---" +ref.get(idx));
String decodeStr = ref.get(idx).getContent().toString();
System.out.println("findDecryptAndReplace---" +decodeStr);
String decrypted = Encode.decode(decodeStr);
replace(idx, refType, decrypted, ref);
}
private int find(String addrType, Reference ref) throws Exception {
Enumeration enu = ref.getAll();
for (int i = 0; enu.hasMoreElements(); i++) {
RefAddr addr = (RefAddr) enu.nextElement();
if (addr.getType().compareTo(addrType) == 0) {
return i;
}
}
throw new Exception("The \"" + addrType
+ "\" name/value pair was not found"
+ " in the Reference object. The reference Object is" + " "
+ ref.toString());
}
private void replace(int idx, String refType, String newValue, Reference ref)
throws Exception {
ref.remove(idx);
ref.add(idx, new StringRefAddr(refType, newValue));
}
}
加密类,可使用自己的加密算法,主要修改encode和decode两个方法,自己可以写个main方法测试加解密功能。代码也是从CSDN找的,连接:
package com.axb.data.factory.util;
public class Encode {
//加密
public static String encode(String password) {
String result = "";
byte[] psd = password.getBytes();
for (int i = 0; i < psd.length; i++) {
result += Integer.toHexString(psd[i] & 0xff);
}
return result;
}
//解密
public static String decode(String password) {
String result = "";
System.out.println("encode---->" +password);
password = password.toUpperCase();
int length = password.length() / 2;
char[] hexChars = password.toCharArray();
byte[] d = new byte[length];
for (int i = 0; i < length; i++) {
int pos = i * 2;
d[i] = (byte) (charToByte(hexChars[pos]) << 4 | charToByte(hexChars[pos + 1]));
}
result = new String(d);
return result;
}
//字符转字节
public static byte charToByte(char c) {
return (byte) "0123456789ABCDEF".indexOf(c);
}
tomcat启动后控制台输出效果:
能正常启动就可以,该方法不需要额外的jar包,开发的时候只需要引入tomcat-dbcp.jar,这个包在tomcat的lib下就有。不需要额外下载。
通过http请求密码
这样改完以后配置文件密码是密文了,但是每次更换密码还是需要修改密文并重启tomcat,当然也可以把密码写在某个路径下的文件中。运维人员每次变更密码都要改,服务器多了也是需要花费不少时间。我就试着用http请求获取密码,改动是在findDecryptAndReplace 方法
private void findDecryptAndReplace(String refType, Reference ref)
throws Exception {
int idx = find(refType, ref);
System.out.println(idx +"----->findDecryptAndReplace---" +ref.get(idx));
String decodeStr = ref.get(idx).getContent().toString();
System.out.println("findDecryptAndReplace---" +decodeStr);
String decrypted = Encode.decode(decodeStr);
// replace(idx, refType, decrypted, ref);
/**
* 试试远程获取密码
*/
String url = "http://localhost:9002/getDataBaseInfo";
String jsonData = "{\"userId\":\"zhangsan\"}";
String returnStr = HttpUtil.httpPost(url,jsonData);
JSONObject tokenJSON = JSONObject.fromObject(returnStr);
System.out.println("decrypted:==="+decrypted +"json_password"+tokenJSON.get("password"));
System.out.println(tokenJSON.get("password")+"===="+tokenJSON.get("userName"));
replace(idx, refType, tokenJSON.get("password").toString(), ref);
}
启动后控制台日志:
http请求需要多引入一些jar包:
jar包直接扔到tomcat的lib目录下就可以。
测试方法类也简单
@RequestMapping(value = "/getDataBaseInfo", method = RequestMethod.POST)
public String getDataBaseInfo(@RequestBody String josnString) {
System.out.println("=================josnString="+josnString);
Map m = new HashMap();
m.put("userName","root");
m.put("password","root");
Gson gson = new Gson();
gson.toJson(m);
return gson.toJson(m);
}
这种方式可以请求少修改配置文件,但还是需要重启服务器的,因为我试着修改密码为错误密码,应用还是能正常访问的。
代码链接:
https://download.csdn.net/download/jiudihanbing/87537958
没积分也可以直接粘贴文章中代码,有积分可以下载代码直接使用,测试代码因为很简单就没有了
上面的方法都不能让运维人员省时省力的解决问题,只是稍微合规和减小点工作量。
我公司有特权账号的产品可以让Tomact context.xml配置文件通过接口获取最新DB密码,同时也不需要重启tomcat,我以后有机会在了解一下实现机制。