思路
com.alibaba.druid.pool.DruidAbstractDataSource.createPhysicalConnection
方法中涎劈,通過userCallback和passwordCallback可以對用戶名密碼進(jìn)行自定義操作读恃,內(nèi)容如下
public PhysicalConnectionInfo createPhysicalConnection() throws SQLException {
***
String user;
if (getUserCallback() != null) {
user = getUserCallback().getName();
} else {
user = getUsername();
}
String password = getPassword();
PasswordCallback passwordCallback = getPasswordCallback();
if (passwordCallback != null) {
if (passwordCallback instanceof DruidPasswordCallback) {
DruidPasswordCallback druidPasswordCallback = (DruidPasswordCallback) passwordCallback;
druidPasswordCallback.setUrl(url);
druidPasswordCallback.setProperties(connectProperties);
}
char[] chars = passwordCallback.getPassword();
if (chars != null) {
password = new String(chars);
}
}
***
}
userCallback和passwordCallback分別為
import javax.security.auth.callback.NameCallback;
import javax.security.auth.callback.PasswordCallback;
protected volatile PasswordCallback passwordCallback;
protected volatile NameCallback userCallback;
public PasswordCallback getPasswordCallback() {
return passwordCallback;
}
public void setPasswordCallback(PasswordCallback passwordCallback) {
this.passwordCallback = passwordCallback;
}
public void setPasswordCallbackClassName(String passwordCallbackClassName) throws Exception {
Class<?> clazz = Utils.loadClass(passwordCallbackClassName);
if (clazz != null) {
this.passwordCallback = (PasswordCallback) clazz.newInstance();
} else {
LOG.error("load passwordCallback error : " + passwordCallbackClassName);
this.passwordCallback = null;
}
}
public NameCallback getUserCallback() {
return userCallback;
}
public void setUserCallback(NameCallback userCallback) {
this.userCallback = userCallback;
}
直接引用druid情況
如果是直接引用druid而非autoconfig形式(即非druid-spring-boot-starter形式引入)
可以直接繼承
om.alibaba.druid.pool.DruidDataSource
直接重寫兩個對兩個callback進(jìn)行賦值即可
spring.datasource.type=你自己實現(xiàn)的類
xml配置的情況
可以配置
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
***
<property name="userCallback" ref="dbUserCallback"/>
<property name="passwordCallback" ref="dbPasswordCallback"/>
***
</bean>
本文重點:application.yml的情況
因為yml無法引用bean或?qū)ο?本人沒有找到對應(yīng)方法享完,如果有大神知道截亦,請告知)污筷,且autoconfig是通過druiddatasourcewrapper來注入?yún)?shù)的渴逻。并且druid只提供了passwordCallback的className setter方法袍患,因此只能做到對password的自定義加密解密,配置方法如下:
datasource:
type: com.alibaba.druid.pool.DruidDataSource
druid:
passwordCallbackClassName: 你自定義的類全限定名
想要同時加密用戶名密碼就要用到filter代碼入下:
application.yml
spring:
datasource:
type: com.alibaba.druid.pool.DruidDataSource
druid:
# 略過
publicKey: aabbccdd
db1: #數(shù)據(jù)源1
driverClassName: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/1
username: admin
password: admin
connection-properties: publicKey=${spring.datasource.druid.publicKey};username=${spring.datasource.druid.db1.username};password=${spring.datasource.druid.db1.password}
db2: #數(shù)據(jù)源2
driverClassName: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/2
username: admin
password: admin
connection-properties: publicKey=${spring.datasource.druid.publicKey};username=${spring.datasource.druid.db2.username};password=${spring.datasource.druid.db2.password}
db3: #數(shù)據(jù)源3
driverClassName: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/3
username: admin
password: admin
connection-properties: publicKey=${spring.datasource.druid.publicKey};username=${spring.datasource.druid.db3.username};password=${spring.datasource.druid.db3.password}
db4: #數(shù)據(jù)源4 數(shù)棧的tidb庫攻泼,統(tǒng)計指標(biāo)用
driverClassName: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/4
username: admin
password: admin
connection-properties: publicKey=${spring.datasource.druid.publicKey};username=${spring.datasource.druid.db4.username};password=${spring.datasource.druid.db4.password}
filter
import com.alibaba.druid.filter.FilterEventAdapter;
@Component
public class DataSourceFilter extends FilterEventAdapter {
@Override
public void init(DataSourceProxy dataSourceProxy) {
DruidDataSource dataSource = (DruidDataSource) dataSourceProxy;
dataSource.setUserCallback(new MyDruidUsernameCallback(dataSource.getConnectProperties()));
dataSource.setPasswordCallback(new MyDruidPasswordCallback());
super.init(dataSource);
}
}
MyDruidPasswordCallback
import com.alibaba.druid.util.DruidPasswordCallback;
public class MyDruidPasswordCallback extends DruidPasswordCallback {
@Override
public void setProperties(Properties properties) {
super.setProperties(properties);
//獲取application.yml 里面配置的密碼和公鑰
String password = (String) properties.get("password");
String publicKey = (String) properties.get("publicKey");
if (password == null) return;
try {
// SMHelper 為自己的解密工具
String decryptPassword = SMHelper.sm4Decrypt(publicKey, password);
setPassword(decryptPassword.toCharArray());
} catch (Exception e) {
log.error("Druid ConfigTools.decrypt", e);
}
}
}
MyDruidUsernameCallback
import javax.security.auth.callback.NameCallback;
public class MyDruidUsernameCallback extends NameCallback {
private final Properties properties;
public MyDruidUsernameCallback(Properties properties) {
super("no uses, i write aimlessly");
this.properties = properties;
}
@Override
public String getName() {
String username = (String) properties.get("username");
String publicKey = (String) properties.get("publicKey");
try {
// SMHelper 為自己的解密工具
String decryptUserName = SMHelper.sm4Decrypt(publicKey, username);
super.setName(decryptUserName);
} catch (Exception e) {
log.error("Druid ConfigTools.decrypt", e);
}
return super.getName();
}
}
備注
如果可以修改druid源碼的話火架,github上有老哥給出了其他解決方案:
支持自定義password-callback & add test case by JoeyBling · Pull Request #3877 · alibaba/druid · GitHub