diff --git a/src/main/java/com/alibaba/druid/filter/config/ConfigFilter.java b/src/main/java/com/alibaba/druid/filter/config/ConfigFilter.java index 7f06391442..adf6c10e50 100644 --- a/src/main/java/com/alibaba/druid/filter/config/ConfigFilter.java +++ b/src/main/java/com/alibaba/druid/filter/config/ConfigFilter.java @@ -15,225 +15,210 @@ */ package com.alibaba.druid.filter.config; -import java.io.File; -import java.io.FileInputStream; -import java.io.InputStream; -import java.net.URL; -import java.security.KeyFactory; -import java.security.PublicKey; -import java.security.spec.X509EncodedKeySpec; -import java.util.Properties; - -import javax.crypto.Cipher; - import com.alibaba.druid.filter.FilterAdapter; import com.alibaba.druid.pool.DruidDataSource; import com.alibaba.druid.pool.DruidDataSourceFactory; import com.alibaba.druid.proxy.jdbc.DataSourceProxy; import com.alibaba.druid.support.logging.Log; import com.alibaba.druid.support.logging.LogFactory; -import com.alibaba.druid.util.Base64; -import com.alibaba.druid.util.JdbcUtils; +import com.alibaba.druid.support.security.decryptor.DecryptException; +import com.alibaba.druid.support.security.decryptor.Decrypter; +import com.alibaba.druid.support.security.decryptor.DecrypterFactory; +import com.alibaba.druid.support.security.decryptor.SensitiveParameters; +import java.sql.SQLException; +import java.util.Properties; + +/** + *
+ * 这个类主要是负责两个事情, 解密, 和下载远程的配置文件
+ * [解密]
+ *
+ * DruidDataSource dataSource = new DruidDataSource();
+ * //dataSource.setXXX 其他设置
+ * //下面两步很重要
+ * //启用config filter
+ * dataSource.setFilters("config");
+ * //使用RSA解密(使用默认密钥)
+ * dataSource.setConnectionPropertise("config.decrypt=RSA");
+ * dataSource.setPassword("加密的密文");
+ *
+ * [远程配置文件]
+ * DruidDataSource dataSource = new DruidDataSource();
+ * //下面两步很重要
+ * //启用config filter
+ * dataSource.setFilters("config");
+ * //使用RSA解密(使用默认密钥)
+ * dataSource.setConnectionPropertise("config.file=http://localhost:8080/remote.propreties;");
+ *
+ * [Spring的配置解密]
+ *
+ * <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
+ *     <property name="password" value="加密的密文" />
+ *     <!-- 其他的属性设置 -->
+ *     <property name="filters" value="config" />
+ *     <property name="connectionProperties" value="config.decrypt=RSA" />
+ * </bean>
+ *
+ * [Spring的配置远程配置文件]
+ *
+ * <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
+ *     <property name="filters" value="config" />
+ *     <property name="connectionProperties" value="config.file=http://localhost:8080/remote.propreties; />
+ * </bean>
+ *
+ * 远程配置文件格式:
+ * 1. 其他的属性KEY请查看 @see com.alibaba.druid.pool.DruidDataSourceFactory
+ * 2. config filter 相关设置:
+ * config.xxxxx=yyyyy
+ *
+ * 
+ */ public class ConfigFilter extends FilterAdapter { - public final static String URL_PREFIX = "druid-configFile="; - public final static String SYS_PROP_CONFIG_FILE = "druid.config.file"; - public final static String SYS_PROP_CONFIG_KEY = "druid.config.key"; - public final static String SYS_PROP_CONFIG_ENCRYPTED_PASSWORD = "druid.config.encryptedPassword"; - public final static String DEFAULT_ALGORITHM = "RSA"; + private static Log log = LogFactory.getLog(ConfigFilter.class); - private final static Log LOG = LogFactory.getLog(ConfigFilter.class); + public final static String CONFIG_FILE = "config.file"; - private String file; - - private Cipher cipher; - private String algorithm; - private String key; - private String encryptedPassword; + public final static String CONFIG_DECRYPT = "config.decrypt"; public ConfigFilter(){ - this.setAlgorithm(DEFAULT_ALGORITHM); - - { - String property = System.getProperty(SYS_PROP_CONFIG_KEY); - if (property != null && property.length() != 0) { - this.setKey(property); - } - } - { - String property = System.getProperty(SYS_PROP_CONFIG_ENCRYPTED_PASSWORD); - if (property != null && property.length() != 0) { - this.setEncryptedPassword(property); - } - } - } - - public String getAlgorithm() { - return algorithm; - } - - public final void setAlgorithm(String algorithm) { - this.algorithm = algorithm; - try { - this.cipher = Cipher.getInstance(algorithm); - } catch (Exception e) { - throw new IllegalArgumentException("illegal algorithm", e); - } - } - - public String getEncryptedPassword() { - return encryptedPassword; - } - - public final void setEncryptedPassword(String encryptedPassword) { - this.encryptedPassword = encryptedPassword; - } - - public String getKey() { - return key; - } - - public final void setKey(String key) { - this.key = key; - - byte[] bytes = Base64.base64ToByteArray(key); - - try { - if (cipher.getAlgorithm().startsWith("RSA")) { - X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(bytes); - KeyFactory keyFactory = KeyFactory.getInstance("RSA"); - PublicKey publicKey = keyFactory.generatePublic(x509KeySpec); - cipher.init(Cipher.DECRYPT_MODE, publicKey); - } - } catch (Exception e) { - throw new IllegalArgumentException("illegal key", e); - } } public void init(DataSourceProxy dataSourceProxy) { if (!(dataSourceProxy instanceof DruidDataSource)) { - LOG.error("ConfigLoader only support DruidDataSource"); + log.error("ConfigLoader only support DruidDataSource"); } + Properties info = null; DruidDataSource dataSource = (DruidDataSource) dataSourceProxy; + Properties connectinProperties = dataSource.getConnectProperties(); - Properties properties = loadConfig(dataSource); + //获取远程配置文件 + String protocol = connectinProperties.getProperty(CONFIG_FILE); - if (encryptedPassword != null) { - try { - byte[] passwordBytes = Base64.base64ToByteArray(encryptedPassword); - byte[] decryptedBytes = cipher.doFinal(passwordBytes); - String decryptedPassword = new String(decryptedBytes, "ISO-8859-1"); - dataSource.setPassword(decryptedPassword); - } catch (Exception e) { - LOG.error("decrypt password error", e); + if (protocol != null) { + if (log.isDebugEnabled()) { + log.debug("Config file will be load from [" + protocol + "]."); + } + + //获取配置文件内容 + ConfigLoader configLoader = ConfigLoaderFactory.getConfigLoader(protocol); + + if (configLoader == null) { + throw new IllegalArgumentException("Druid doesn't support the [config.file=" + protocol + "] to load remote config file."); + } + + info = configLoader.loadConfig(protocol); + + if (info == null) { + throw new IllegalArgumentException("Cannot load remote config file from the [config.file=" + protocol + "]."); + } + } else { + info = new Properties(); + if (log.isDebugEnabled()) { + log.debug("No remote config file."); } } - if (properties == null) { - if (encryptedPassword == null) { - LOG.error("load config error, return null"); + //判断是否需要解密,如果需要就进行解密行动 + String decrypterId = info.getProperty(CONFIG_DECRYPT); + boolean isRemotedSecurityConfig = true; + + if (decrypterId == null) { + decrypterId = connectinProperties.getProperty(CONFIG_DECRYPT); + isRemotedSecurityConfig = false; + + if (log.isDebugEnabled()) { + log.debug("Get decrypt method " + decrypterId + " from local config."); + } + } else { + if (log.isDebugEnabled()) { + log.debug("Get decrypt method " + decrypterId + " from remote config."); + } + } + + if (decrypterId != null) { + if (log.isDebugEnabled()) { + log.debug("Use [" + decrypterId + "] to decrypt sensitive parameters."); + } + + try { + Decrypter decrypter = DecrypterFactory.getDecrypter(decrypterId); + if (decrypter == null) { + throw new IllegalArgumentException("Druid doesn't support the decrypter [config.decrypt=" + decrypterId + "]."); + } + + Properties securityInfo = isRemotedSecurityConfig + ? info + : connectinProperties; + + SensitiveParameters originalParameter = getSensitiveParameters(dataSource, info); + SensitiveParameters parameters = decrypter.decrypt(originalParameter, securityInfo); + + info.setProperty(DruidDataSourceFactory.PROP_URL, parameters.getUrl()); + info.setProperty(DruidDataSourceFactory.PROP_USERNAME, parameters.getUsername()); + info.setProperty(DruidDataSourceFactory.PROP_PASSWORD, parameters.getPassword()); + } catch (DecryptException e) { + throw new IllegalArgumentException("Failed to decrypt.", e); + } + + } else { + if (log.isDebugEnabled()) { + log.debug("No decrypt."); + } + } + + //没有任何配置 + if (info.size() == 0) { + if (log.isDebugEnabled()) { + log.debug("No config."); } return; } try { - if (this.cipher != null) { - String password = properties.getProperty(DruidDataSourceFactory.PROP_PASSWORD); - if (password != null && password.length() != 0) { - byte[] passwordBytes = Base64.base64ToByteArray(password); - byte[] decryptedBytes = cipher.doFinal(passwordBytes); - - String decryptedPassword = new String(decryptedBytes, "ISO-8859-1"); - properties.put(DruidDataSourceFactory.PROP_PASSWORD, decryptedPassword); - } - } - } catch (Exception e) { - LOG.error("decrypt password error", e); - } - - try { - DruidDataSourceFactory.config(dataSource, properties); - } catch (Exception e) { - LOG.error("config dataSource error", e); + DruidDataSourceFactory.config(dataSource, info); + } catch (SQLException e) { + throw new IllegalArgumentException("Config DataSource error.", e); } } - public Properties loadConfig(DruidDataSource dataSource) { - String filePath = this.file; + /** + * 如果配置文件中没有值, 就取默认值 + * @param dataSource + * @param info + * @return + */ + SensitiveParameters getSensitiveParameters(DruidDataSource dataSource, Properties info) { + String username = info.getProperty(DruidDataSourceFactory.PROP_USERNAME); + String password = info.getProperty(DruidDataSourceFactory.PROP_PASSWORD); + String url = info.getProperty(DruidDataSourceFactory.PROP_URL); - // jdbc:druid-config: - if (file == null && dataSource.getUrl() != null && dataSource.getUrl().startsWith(ConfigFilter.URL_PREFIX)) { - filePath = dataSource.getUrl().substring(ConfigFilter.URL_PREFIX.length()); + if (username == null) { + username = dataSource.getUsername(); } - if (filePath == null) { - filePath = System.getProperty(SYS_PROP_CONFIG_FILE); + if (password == null) { + password = dataSource.getPassword(); } - if (filePath == null) { - if (this.encryptedPassword == null) { - LOG.error("load config error, file is null"); - } - return null; + if (url == null) { + url = dataSource.getUrl(); } - Properties properties = new Properties(); + return new SensitiveParameters(url, username, password); + } - InputStream inStream = null; - try { - boolean xml = false; - if (filePath.startsWith("http://") || filePath.startsWith("https://")) { - URL url = new URL(filePath); - inStream = url.openStream(); + public int hashCode() { + return this.getClass().hashCode(); + } - xml = url.getPath().endsWith(".xml"); - } else { - File file = new File(filePath); - if (file.exists()) { - inStream = new FileInputStream(file); - } else { - inStream = Thread.currentThread().getContextClassLoader().getResourceAsStream(filePath); - } - - xml = filePath.endsWith(".xml"); - } - - if (inStream == null) { - LOG.error("load config file error, file : " + filePath); - return null; - } - - if (xml) { - properties.loadFromXML(inStream); - } else { - properties.load(inStream); - } - - return properties; - } catch (Exception ex) { - LOG.error("load config file error, file : " + filePath, ex); - return null; - } finally { - JdbcUtils.close(inStream); + public boolean equals(Object obj) { + if (obj == null) { + return false; } - } - public String getFile() { - return file; + return obj.getClass().equals(ConfigFilter.class); } - - public void setFile(String file) { - this.file = file; - } - - public Cipher getCipher() { - return cipher; - } - - public void setCipher(Cipher cipher) { - this.cipher = cipher; - } - } diff --git a/src/main/java/com/alibaba/druid/filter/config/ConfigLoader.java b/src/main/java/com/alibaba/druid/filter/config/ConfigLoader.java new file mode 100644 index 0000000000..118075a077 --- /dev/null +++ b/src/main/java/com/alibaba/druid/filter/config/ConfigLoader.java @@ -0,0 +1,50 @@ +package com.alibaba.druid.filter.config; + +import java.util.Properties; + +/** + *
+ * 配置文件装载器.
+ *
+ * 要实现自己的装载器有如下要求.
+ * 1. 一定要有自己的协议格式, 例如是由 my-protocol: 开头的protocol是由我支持的.
+ * public isSupported(String protocol) {
+ *     return protocol.startsWith("my-protocol:");
+ * }
+ *
+ * 2. 要在class path的根目录下的ConfigLoader.properties注册(如果没有这个文件, 可以自己创建一个). 例如:
+ * WEB-INF/classes/ConfigLoader.properties [格式如下]:
+ * ConfigLoader.myConfigLoader=com.xxx.xxx.xx.MyConfigLoader
+ *
+ * 3. 配置DruidDataSource的时候的URL就要设置为装载器的协议格式
+ * DruidDataSource dataSource = new DruidDataSource();
+ * dataSource.setUrl("my-protocol:http://xxxxxx/");
+ *
+ * 
+ * @see com.alibaba.druid.filter.config.impl.FileConfigLoader + * @see com.alibaba.druid.filter.config.impl.HttpConfigLoader + * + * @author Jonas Yang + */ +public interface ConfigLoader { + + /** + * 唯一的ID + * @return + */ + public String getId(); + + /** + * 获得配置内容, 如果没有, 返回null. + * @param protocol + * @return + */ + public Properties loadConfig(String protocol); + + /** + * 是否支持该协议, 如果支持, 将会使用该装载器获取配置文件 + * @param protocol + * @return + */ + public boolean isSupported(String protocol); +} diff --git a/src/main/java/com/alibaba/druid/filter/config/ConfigLoaderFactory.java b/src/main/java/com/alibaba/druid/filter/config/ConfigLoaderFactory.java new file mode 100644 index 0000000000..a8f8a2ff60 --- /dev/null +++ b/src/main/java/com/alibaba/druid/filter/config/ConfigLoaderFactory.java @@ -0,0 +1,102 @@ +package com.alibaba.druid.filter.config; + +import com.alibaba.druid.support.logging.Log; +import com.alibaba.druid.support.logging.LogFactory; +import com.alibaba.druid.util.JdbcUtils; + +import java.io.InputStream; +import java.util.Properties; +import java.util.concurrent.ConcurrentHashMap; + +/** + *
+ * 如果有扩展的装载器.
+ * 可以放到classes目录下的ConfigLoader.properties
+ *
+ * ConfigLoader.properties 内容如下:
+ * ConfigLoader.x=com.xx.xxx.xx.MyConfigLoader
+ *
+ * 
+ * @author Jonas Yang + */ +public class ConfigLoaderFactory { + + private static Log log = LogFactory.getLog(ConfigLoaderFactory.class); + + private static ConcurrentHashMap configLoaders = new ConcurrentHashMap(); + + static { + //加载默认的装载器 + initConfigLoader("META-INF/druid-config-loader-default.properties", ConfigLoaderFactory.class.getClassLoader()); + + //加载扩展的装载器 + initConfigLoader("druid-config-loader.properties", ClassLoader.getSystemClassLoader()); + + if (ClassLoader.getSystemClassLoader() != Thread.currentThread().getContextClassLoader()) { + initConfigLoader("ConfigLoader.properties", Thread.currentThread().getContextClassLoader()); + } + } + + private ConfigLoaderFactory() {} + + static void initConfigLoader(String resource, ClassLoader classLoader) { + if (resource == null) { + return; + } + + InputStream inStream = null; + + try { + inStream = classLoader.getResourceAsStream(resource); + if (inStream == null) { + return; + } + + Properties p = new Properties(); + p.load(inStream); + + for (String key : p.stringPropertyNames()) { + String clazzName = p.getProperty(key); + System.out.println(clazzName); + Class clazz = classLoader.loadClass(clazzName); + ConfigLoader configLoader = (ConfigLoader) clazz.newInstance(); + + addConfigLoader(configLoader); + } + } catch (Exception e) { + log.warn("Fail to init config loaders.", e); + } finally { + JdbcUtils.close(inStream); + } + } + + /** + * 通过 protocol 获得装载器. 如果没有返回 null. + * @param protocol + * @return 如果没有找到返回null + */ + public static ConfigLoader getConfigLoader(String protocol) { + for (ConfigLoader configLoader : configLoaders.values()) { + if (configLoader.isSupported(protocol)) { + return configLoader; + } + } + + return null; + } + + public static void addConfigLoader(ConfigLoader configLoader) { + if (configLoaders.putIfAbsent(configLoader.getId(), configLoader) != null) { + log.warn("Duplicate config loader [" + configLoader.getId() + "]"); + } else { + if (log.isDebugEnabled()) { + log.debug("Add config loader [" + configLoader.getId() + " = " + configLoader.getClass() + "]."); + } + } + } + + public static ConfigLoader[] getConfigLoaders() { + return configLoaders.values().toArray(new ConfigLoader[0]); + } + +} diff --git a/src/main/java/com/alibaba/druid/filter/config/ConfigTool.java b/src/main/java/com/alibaba/druid/filter/config/ConfigTool.java deleted file mode 100644 index 01b55d7d16..0000000000 --- a/src/main/java/com/alibaba/druid/filter/config/ConfigTool.java +++ /dev/null @@ -1,148 +0,0 @@ -/* - * Copyright 1999-2011 Alibaba Group Holding Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.alibaba.druid.filter.config; - -import java.security.KeyFactory; -import java.security.KeyPair; -import java.security.KeyPairGenerator; -import java.security.PrivateKey; -import java.security.PublicKey; -import java.security.SecureRandom; -import java.security.interfaces.RSAPrivateKey; -import java.security.interfaces.RSAPublicKey; -import java.security.spec.PKCS8EncodedKeySpec; -import java.security.spec.X509EncodedKeySpec; -import java.util.LinkedHashMap; -import java.util.Map; - -import javax.crypto.Cipher; - -import com.alibaba.druid.util.Base64; - -public class ConfigTool { - public final static int DEFAULT_KEY_SIZE = 1024; - - public static void main(String[] args) throws Exception { - if (args.length == 0) { - return; - } - - String command = args[0]; - - if ("-gen".equals(command)) { - int keySize = DEFAULT_KEY_SIZE; - - if (args.length > 1) { - keySize = Integer.parseInt(args[1]); - } - - Map result = gen(keySize); - print(result); - - return; - } - - if ("-encrypt".equals(command)) { - String privateKey = args[1]; - String text = args[2]; - - Map result = encrypt(privateKey, text); - print(result); - - return; - } - - if ("-decrypt".equals(command)) { - String publicKey = args[1]; - String text = args[2]; - - Map result = decrypt(publicKey, text); - print(result); - - return; - } - } - - public static void print(Map map) { - for (Map.Entry entry : map.entrySet()) { - System.out.print(entry.getKey()); - System.out.print(" : "); - System.out.println(); - System.out.print(entry.getValue()); - System.out.println(); - System.out.println(); - } - } - - public static Map decrypt(String publicKeyString, String cipherText) throws Exception { - Map map = new LinkedHashMap(); - - byte[] publicKeyBytes = Base64.base64ToByteArray(publicKeyString); - X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(publicKeyBytes); - - KeyFactory keyFactory = KeyFactory.getInstance("RSA"); - PublicKey publicKey = keyFactory.generatePublic(x509KeySpec); - - Cipher cipher = Cipher.getInstance("RSA"); - cipher.init(Cipher.DECRYPT_MODE, publicKey); - - byte[] cipherBytes = Base64.base64ToByteArray(cipherText); - byte[] plainBytes = cipher.doFinal(cipherBytes); - String plainText = new String(plainBytes); - - map.put("result", plainText); - - return map; - } - - public static Map encrypt(String privateKeyString, String plain) throws Exception { - Map map = new LinkedHashMap(); - - byte[] privateKeyBytes = Base64.base64ToByteArray(privateKeyString); - PKCS8EncodedKeySpec x509KeySpec = new PKCS8EncodedKeySpec(privateKeyBytes); - KeyFactory keyFactory = KeyFactory.getInstance("RSA"); - PrivateKey privateKey = keyFactory.generatePrivate(x509KeySpec); - - Cipher cipher = Cipher.getInstance("RSA"); - cipher.init(Cipher.ENCRYPT_MODE, privateKey); - - byte[] plainBytes = plain.getBytes(); - byte[] cipherBytes = cipher.doFinal(plainBytes); - - String cipherText = Base64.byteArrayToBase64(cipherBytes); - - map.put("result", cipherText); - - return map; - } - - public static Map gen(int keySize) throws Exception { - Map map = new LinkedHashMap(); - - KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA"); - keyPairGen.initialize(512, new SecureRandom()); - - KeyPair keyPair = keyPairGen.generateKeyPair(); - - RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate(); - RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic(); - - map.put("privateKey", Base64.byteArrayToBase64(privateKey.getEncoded())); - map.put("publicKey", Base64.byteArrayToBase64(publicKey.getEncoded())); - - return map; - } -} diff --git a/src/main/java/com/alibaba/druid/filter/config/impl/FileConfigLoader.java b/src/main/java/com/alibaba/druid/filter/config/impl/FileConfigLoader.java new file mode 100644 index 0000000000..3948d759a4 --- /dev/null +++ b/src/main/java/com/alibaba/druid/filter/config/impl/FileConfigLoader.java @@ -0,0 +1,104 @@ +package com.alibaba.druid.filter.config.impl; + +import com.alibaba.druid.filter.config.ConfigLoader; +import com.alibaba.druid.support.logging.Log; +import com.alibaba.druid.support.logging.LogFactory; +import com.alibaba.druid.util.JdbcUtils; + +import java.io.File; +import java.io.FileInputStream; +import java.io.InputStream; +import java.util.Properties; + +/** + *
+ * 通过指定的配置文件路径, 加载该配置. 使用使用NFS共享文件等情况.
+ * DruidDataSource的connectionProperties必须是 config.file=file: 开头.
+ * 注意: OSGI下的跨bundle获取资源, 没有测试过
+ * 
+ * @author Jonas Yang + */ +public class FileConfigLoader implements ConfigLoader { + + private static Log LOG = LogFactory.getLog(FileConfigLoader.class); + + public static final String PROTOCOL_PREFIX = "file:"; + public static final String SYS_PROP_CONFIG_FILE = "druid.config.file"; + + @Override + public String getId() { + return PROTOCOL_PREFIX; + } + + /** + * 如果没有找到配置文件, 返回null + * @param protocol + * @return 如果有找到配置文件返回null + */ + public Properties loadConfig(String protocol) { + if (!isSupported(protocol)) { + throw new IllegalArgumentException("The protocol [" + protocol + "] is not accepted. Protocol must start with [" + PROTOCOL_PREFIX + "]."); + } + + if(LOG.isDebugEnabled()) { + LOG.debug("Load config file[" + protocol + "] by [" + getId() + "]"); + } + + Properties properties = new Properties(); + InputStream inStream = null; + + try { + String filePath = getFilePath(protocol); + File file = new File(filePath); + + if (file.exists()) { + inStream = new FileInputStream(file); + } else { + inStream = Thread.currentThread().getContextClassLoader().getResourceAsStream(filePath); + } + + if (inStream == null) { + LOG.error("load config file error, file : " + protocol); + return null; + } + + if (isXml(filePath)) { + properties.loadFromXML(inStream); + } else { + properties.load(inStream); + } + + return properties; + } catch (Exception ex) { + LOG.error("load config file error, file : " + protocol, ex); + return null; + } finally { + JdbcUtils.close(inStream); + } + } + + String getFilePath(String protocol) { + String filePath = null; + + if(protocol == null) { + filePath = System.getProperty(SYS_PROP_CONFIG_FILE); + } else { + filePath = protocol.substring(PROTOCOL_PREFIX.length()); + } + + return filePath; + } + + boolean isXml(String filePath) { + return filePath.endsWith(".xml"); + } + + @Override + public boolean isSupported(String protocol) { + if (protocol == null) { + return System.getProperty(SYS_PROP_CONFIG_FILE) != null; + } + + return protocol.toLowerCase().startsWith(PROTOCOL_PREFIX); + } +} diff --git a/src/main/java/com/alibaba/druid/filter/config/impl/HttpConfigLoader.java b/src/main/java/com/alibaba/druid/filter/config/impl/HttpConfigLoader.java new file mode 100644 index 0000000000..9e47e38a02 --- /dev/null +++ b/src/main/java/com/alibaba/druid/filter/config/impl/HttpConfigLoader.java @@ -0,0 +1,88 @@ +package com.alibaba.druid.filter.config.impl; + +import com.alibaba.druid.filter.config.ConfigLoader; +import com.alibaba.druid.support.logging.Log; +import com.alibaba.druid.support.logging.LogFactory; +import com.alibaba.druid.util.JdbcUtils; + +import java.io.InputStream; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.Properties; + +/** + *
+ * 通过HTTP方式获取资源文件.
+ * DruidDataSource的connectionProperties必须是 config.file=http:// 开头.
+ * 
+ * @author Jonas Yang + */ +public class HttpConfigLoader implements ConfigLoader { + private static Log LOG = LogFactory.getLog(HttpConfigLoader.class); + + public final static String PROTOCOL_PREFIX = "http://"; + + @Override + public String getId() { + return PROTOCOL_PREFIX; + } + + /** + * 如果没有找到资源文件, 返回null + * @param protocol + * @return 如果没有找到资源文件, 返回null + */ + public Properties loadConfig(String protocol) { + if (!isSupported(protocol)) { + throw new IllegalArgumentException("The protocol [" + protocol + "] is not accepted. Protocol must start with [" + PROTOCOL_PREFIX + "]."); + } + + if(LOG.isDebugEnabled()) { + LOG.debug("Load config file[" + protocol + "] by [" + getId() + "]"); + } + + Properties properties = new Properties(); + + InputStream inStream = null; + try { + URL url = getUrl(protocol); + inStream = url.openStream(); + + if (inStream == null) { + LOG.error("load config file error, file : " + protocol); + return null; + } + + if (isXml(url)) { + properties.loadFromXML(inStream); + } else { + properties.load(inStream); + } + + return properties; + } catch (Exception ex) { + LOG.error("load config file error, file : " + protocol, ex); + return null; + } finally { + JdbcUtils.close(inStream); + } + } + + URL getUrl(String protocol) throws MalformedURLException { + return new URL(protocol.substring(PROTOCOL_PREFIX.length() - 7)); + } + + boolean isXml(URL url) { + return url.getPath().endsWith(".xml"); + + } + + @Override + public boolean isSupported(String protocol) { + if (protocol == null) { + return false; + } + + return protocol.toLowerCase().startsWith(PROTOCOL_PREFIX); + } +} diff --git a/src/main/java/com/alibaba/druid/pool/DecryptException.java b/src/main/java/com/alibaba/druid/pool/DecryptException.java deleted file mode 100644 index e84e568d8d..0000000000 --- a/src/main/java/com/alibaba/druid/pool/DecryptException.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.alibaba.druid.pool; - -/** - * 加密出错 - * - * @author Jonas Yang - */ -public class DecryptException extends Exception { - public DecryptException() { - super(); //To change body of overridden methods use File | Settings | File Templates. - } - - public DecryptException(String s) { - super(s); //To change body of overridden methods use File | Settings | File Templates. - } - - public DecryptException(String s, Throwable throwable) { - super(s, throwable); //To change body of overridden methods use File | Settings | File Templates. - } - - public DecryptException(Throwable throwable) { - super(throwable); //To change body of overridden methods use File | Settings | File Templates. - } -} diff --git a/src/main/java/com/alibaba/druid/pool/DruidAbstractDataSource.java b/src/main/java/com/alibaba/druid/pool/DruidAbstractDataSource.java index 87680676a1..649fb05f57 100644 --- a/src/main/java/com/alibaba/druid/pool/DruidAbstractDataSource.java +++ b/src/main/java/com/alibaba/druid/pool/DruidAbstractDataSource.java @@ -221,16 +221,6 @@ public abstract class DruidAbstractDataSource extends WrapperAdapter implements protected AtomicLong createCount = new AtomicLong(); protected AtomicLong destroyCount = new AtomicLong(); - protected Decrypter decrypter; - - public Decrypter getDecrypter() { - return decrypter; - } - - public void setDecrypter(Decrypter decrypter) { - this.decrypter = decrypter; - } - public boolean isOracle() { return isOracle; } @@ -934,9 +924,9 @@ public abstract class DruidAbstractDataSource extends WrapperAdapter implements this.jdbcUrl = jdbcUrl; - if (jdbcUrl.startsWith(ConfigFilter.URL_PREFIX)) { - this.filters.add(new ConfigFilter()); - } +// if (jdbcUrl.startsWith(ConfigFilter.URL_PREFIX)) { +// this.filters.add(new ConfigFilter()); +// } } public String getDriverClassName() { @@ -1247,18 +1237,6 @@ public abstract class DruidAbstractDataSource extends WrapperAdapter implements } } - Decrypter decrypter1 = this.getDecrypter(); - if (decrypter1 != null) { - try { - SensitiveParameters parameters = decrypter1.decrypt(new SensitiveParameters(url, user, password)); - url = parameters.getUrl(); - user = parameters.getUsername(); - password = parameters.getPassword(); - } catch (DecryptException e) { - throw new SQLException("Failed to decrypt sensitive parameters.", e); - } - } - Properties physicalConnectProperties = new Properties(); if (connectProperties != null) { physicalConnectProperties.putAll(connectProperties); diff --git a/src/main/java/com/alibaba/druid/support/security/decryptor/AbstractDecrypter.java b/src/main/java/com/alibaba/druid/support/security/decryptor/AbstractDecrypter.java index ec3e6e2169..0f4e47d0b4 100644 --- a/src/main/java/com/alibaba/druid/support/security/decryptor/AbstractDecrypter.java +++ b/src/main/java/com/alibaba/druid/support/security/decryptor/AbstractDecrypter.java @@ -1,9 +1,9 @@ package com.alibaba.druid.support.security.decryptor; -import com.alibaba.druid.pool.Decrypter; import com.alibaba.druid.util.Base64; import javax.crypto.Cipher; +import java.util.Properties; /** * 加密抽象类 @@ -17,6 +17,8 @@ public abstract class AbstractDecrypter implements Decrypter { */ public static final String KEY_PADDING = "FOLLOW YOUR HEART. YOU CAN DO BEST THAN ANY ONE."; + public final static String KEY = "config.decrypt.key"; + /** * 传入密文, 返回明文 * @param cipher 解密器 diff --git a/src/main/java/com/alibaba/druid/support/security/decryptor/AesDecrypter.java b/src/main/java/com/alibaba/druid/support/security/decryptor/AesDecrypter.java index 304326e3d6..ba0c9ff8a5 100644 --- a/src/main/java/com/alibaba/druid/support/security/decryptor/AesDecrypter.java +++ b/src/main/java/com/alibaba/druid/support/security/decryptor/AesDecrypter.java @@ -1,27 +1,42 @@ package com.alibaba.druid.support.security.decryptor; -import com.alibaba.druid.pool.DecryptException; -import com.alibaba.druid.pool.SensitiveParameters; +import com.alibaba.druid.support.logging.Log; +import com.alibaba.druid.support.logging.LogFactory; import javax.crypto.Cipher; import javax.crypto.spec.SecretKeySpec; +import java.util.Properties; /** - * AES 解密器 + *
+ * AES 解密器, 如果不指定密钥, 使用默认的密钥.
+ * 通过一下方式指定密钥:
+ * Properties info = new Properties();
+ * info.put("config.decrypt.key", "密钥");
+ *
+ * String 明文 = new AesDecrypter().decrypt("密文", info);
+ * 
* * @author Jonas Yang */ public class AesDecrypter extends AbstractDecrypter { + private static Log log = LogFactory.getLog(AesDecrypter.class); private final static int KEY_LENGTH = 16; //128 - private volatile SecretKeySpec spec; + public String getId() { + return "AES"; + } - public SensitiveParameters decrypt(SensitiveParameters parameters) throws DecryptException { - SecretKeySpec spec = getSpec(); - if (spec == null) { - throw new DecryptException("No key."); - } + /** + * 密钥 放在 Properties 参数中, key 是 config.decrypt.key, 如果不指定密钥, 将使用默认密钥 + * @param parameters 密文参数 + * @param info + * @return + * @throws DecryptException + */ + public SensitiveParameters decrypt(SensitiveParameters parameters, Properties info) throws DecryptException { + SecretKeySpec spec = getSpec(info); try { Cipher cipher = Cipher.getInstance("AES"); @@ -35,24 +50,25 @@ public class AesDecrypter extends AbstractDecrypter { } } - protected SecretKeySpec getSpec() { - if (this.spec == null) { - setKey(""); + SecretKeySpec getSpec(Properties info) { + String key = null; + + if (info != null) { + key = info.getProperty(KEY); } - return this.spec; - } - - public void setKey(String key) { if (key == null) { - throw new IllegalArgumentException("The key cannot be null"); + if (log.isDebugEnabled()) { + log.debug("Decrypt by default key"); + } + key = ""; } if (key.length() < 16) { key = KEY_PADDING.substring(0, KEY_LENGTH - key.length()) + key; } - this.spec = new SecretKeySpec(key.getBytes(), "AES"); + return new SecretKeySpec(key.getBytes(), "AES"); } } diff --git a/src/main/java/com/alibaba/druid/support/security/decryptor/DecryptException.java b/src/main/java/com/alibaba/druid/support/security/decryptor/DecryptException.java new file mode 100644 index 0000000000..6bed53ee1f --- /dev/null +++ b/src/main/java/com/alibaba/druid/support/security/decryptor/DecryptException.java @@ -0,0 +1,24 @@ +package com.alibaba.druid.support.security.decryptor; + +/** + * 加密出错 + * + * @author Jonas Yang + */ +public class DecryptException extends Exception { + public DecryptException() { + super(); + } + + public DecryptException(String s) { + super(s); + } + + public DecryptException(String s, Throwable throwable) { + super(s, throwable); + } + + public DecryptException(Throwable throwable) { + super(throwable); + } +} diff --git a/src/main/java/com/alibaba/druid/pool/Decrypter.java b/src/main/java/com/alibaba/druid/support/security/decryptor/Decrypter.java similarity index 52% rename from src/main/java/com/alibaba/druid/pool/Decrypter.java rename to src/main/java/com/alibaba/druid/support/security/decryptor/Decrypter.java index d4a8325681..eba5ad061c 100644 --- a/src/main/java/com/alibaba/druid/pool/Decrypter.java +++ b/src/main/java/com/alibaba/druid/support/security/decryptor/Decrypter.java @@ -1,23 +1,18 @@ -package com.alibaba.druid.pool; +package com.alibaba.druid.support.security.decryptor; + +import java.util.Properties; /** *
  * 解密敏感参数
- * 使用解密器 只要简单实现这个类就可以了。
- *
- * public class MyDecrypter implement Decrypter {
- *     public SensitiveParameters decrypt(SensitiveParameters parameters) throws DecryptException {
- *         String url = ....;
- *         String username = ....;
- *         String password = ....;
- *
- *         return new SensitiveParameters(url, username, password);
- *     }
- * }
+ * [代码例子]
  *
  * DruidDataSource dataSource = new DruidDataSource();
  * //设置DataSource的其他参数
- * dataSource.setDecrypter(new MyDecrypter);
+ * dataSource.setFilters("config");
+ * dataSource.setUsername("test");
+ * dataSource.setPassword("加密的密码");
+ * dataSource.setConnectionProperties("config.decrypt=RSA");
  *
  * [下面是Spring的配置]
  *
@@ -25,25 +20,31 @@ package com.alibaba.druid.pool;
  *     <property name="url" value="${jdbc_url}" />
  *     <property name="username" value="${jdbc_user}" />
  *     <property name="password" value="${jdbc_password}" />
- *     <property name="decrypter"><ref bean="myDecrypter"/></property>
+ *     <property name="filters" value="config" />
+ *     <property name="connectionProperties" value="config.decrypt=RSA" />
  * </bean>
  *
- * <bean id="myDecrypter" class="MyDecrypter"/>
- *
  * 
*
- * 默认下, Druid 已经实现了AES, DES, RSA, Blowfish(兼容JBOSS)等加解密. AES, DES 解密的密文必须是通过Druid Tool的加密工具生成的 + * 默认下, Druid 已经实现了AES, RSA加解密. AES, RSA 密文必须是通过Druid Tool的加密工具生成的 + * 获取默认解密方式可以通过 DecrypterFactory.getDecrypter(String id) *
* 使用加密工具: java -cp druid-x.x.x.jar com.alibaba.druid.support.security.tool.Main * @author Jonas Yang */ public interface Decrypter { + /** + * 标识符 + * @return + */ + public String getId(); + /** * 返回一个解密后的(明文)参数 * @param parameters 密文参数 * @return * @throws DecryptException 解密失败会抛出该异常 */ - public SensitiveParameters decrypt(SensitiveParameters parameters) throws DecryptException; + public SensitiveParameters decrypt(SensitiveParameters parameters, Properties info) throws DecryptException; } diff --git a/src/main/java/com/alibaba/druid/support/security/decryptor/DecrypterFactory.java b/src/main/java/com/alibaba/druid/support/security/decryptor/DecrypterFactory.java new file mode 100644 index 0000000000..f94dcc88c1 --- /dev/null +++ b/src/main/java/com/alibaba/druid/support/security/decryptor/DecrypterFactory.java @@ -0,0 +1,78 @@ +package com.alibaba.druid.support.security.decryptor; + +import com.alibaba.druid.support.logging.Log; +import com.alibaba.druid.support.logging.LogFactory; + +import java.util.concurrent.ConcurrentHashMap; + +/** + * 通过 ID 获取解密器 + * @author Jonas Yang + */ +public class DecrypterFactory { + + private static Log log = LogFactory.getLog(DecrypterFactory.class); + + private static ConcurrentHashMap decrypters = new ConcurrentHashMap(); + + static { + RsaDecrypter rsa = new RsaDecrypter(); + AesDecrypter aes = new AesDecrypter(); + + decrypters.put(rsa.getId(), rsa); + decrypters.put(aes.getId(), aes); + } + + private DecrypterFactory() {} + + +// static void initDecrypters(String resource, ClassLoader classLoader) { +// if (resource == null) { +// return; +// } +// +// InputStream inStream = null; +// +// try { +// inStream = classLoader.getResourceAsStream(resource); +// if (inStream == null) { +// return; +// } +// +// Properties p = new Properties(); +// p.load(inStream); +// +// for (String key : p.stringPropertyNames()) { +// String clazzName = p.getProperty(key); +// Class clazz = classLoader.loadClass(clazzName); +// Decrypter decrypter = (Decrypter) clazz.newInstance(); +// +// addDecrypter(decrypter); +// } +// } catch (Exception e) { +// log.warn("Fail to init config loaders.", e); +// } finally { +// JdbcUtils.close(inStream); +// } +// } + + /** + * 通过 ID 获取解密器, 如果没有找到, 返回 null + * @param id + * @return + */ + public static Decrypter getDecrypter(String id) { + return decrypters.get(id); + } + + public static void addDecrypter(Decrypter decrypter) { + Decrypter oldDecrypter = decrypters.putIfAbsent(decrypter.getId(), decrypter); + if (oldDecrypter != null) { + log.warn("Replace decrypter [" + decrypter.getId() + ", " + oldDecrypter.getClass() + "] with [" + decrypter.getClass() + "]"); + } + } + + public static Decrypter[] getDecrypters() { + return decrypters.values().toArray(new Decrypter[0]); + } +} diff --git a/src/main/java/com/alibaba/druid/support/security/decryptor/RsaDecrypter.java b/src/main/java/com/alibaba/druid/support/security/decryptor/RsaDecrypter.java index eaba0909fd..e3179906d3 100644 --- a/src/main/java/com/alibaba/druid/support/security/decryptor/RsaDecrypter.java +++ b/src/main/java/com/alibaba/druid/support/security/decryptor/RsaDecrypter.java @@ -1,10 +1,9 @@ package com.alibaba.druid.support.security.decryptor; -import com.alibaba.druid.pool.DecryptException; -import com.alibaba.druid.pool.SensitiveParameters; import com.alibaba.druid.support.logging.Log; import com.alibaba.druid.support.logging.LogFactory; import com.alibaba.druid.util.Base64; +import com.alibaba.druid.util.JdbcUtils; import javax.crypto.Cipher; import java.io.ByteArrayOutputStream; @@ -15,6 +14,7 @@ import java.security.PublicKey; import java.security.cert.Certificate; import java.security.cert.CertificateFactory; import java.security.spec.X509EncodedKeySpec; +import java.util.Properties; /** * RSA 解密器, 支持证书 @@ -24,12 +24,32 @@ public class RsaDecrypter extends AbstractDecrypter { private static Log log = LogFactory.getLog(RsaDecrypter.class); - private static final String DEFAULT_PUBLIC_KEY_STRING = "MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKHGwq7q2RmwuRgKxBypQHw0mYu4BQZ3eMsTrdK8E6igRcxsobUC7uT0SoxIjl1WveWniCASejoQtn/BY6hVKWsCAwEAAQ=="; + public static final String KEY = "config.decrypt.key"; + public static final String KEY_FILE = "config.decrypt.keyFile"; + public static final String X509_FILE = "config.decrypt.x509File"; - private volatile PublicKey publicKey; + protected static final String DEFAULT_PUBLIC_KEY_STRING = "MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKHGwq7q2RmwuRgKxBypQHw0mYu4BQZ3eMsTrdK8E6igRcxsobUC7uT0SoxIjl1WveWniCASejoQtn/BY6hVKWsCAwEAAQ=="; - public SensitiveParameters decrypt(SensitiveParameters parameters) throws DecryptException { - PublicKey publicKey = getPublicKey(); + public String getId() { + return "RSA"; + } + + /** + *
+     * 密钥 放在 Properties 参数中,
+     * 如果是密钥字符串, key 是 config.decrypt.key
+     * 如果是密钥文件, key 是 config.decrypt.keyFile
+     * 如果是证书文件, key 是 config.decrypt.x509File
+     *
+     * 如果不指定密钥, 将使用默认密钥
+     * 
+ * @param parameters 密文参数 + * @param info + * @return + * @throws DecryptException + */ + public SensitiveParameters decrypt(SensitiveParameters parameters, Properties info) throws DecryptException { + PublicKey publicKey = getPublicKey(info); try { Cipher cipher = Cipher.getInstance("RSA"); @@ -40,21 +60,53 @@ public class RsaDecrypter extends AbstractDecrypter { } catch (DecryptException e) { throw e; } catch (Exception e) { - throw new DecryptException("Failed to decrypt JDBC parameters", e); + throw new DecryptException("Failed to decrypt parameters", e); } } - protected PublicKey getPublicKey() { - if (this.publicKey == null) { - setPublicKeyString(DEFAULT_PUBLIC_KEY_STRING); + /** + * 通过指定 + * @param info + * @return + */ + PublicKey getPublicKey(Properties info) { + String key = null; + String publicKeyFile = null; + String x509File = null; + + if (info != null) { + key = info.getProperty(KEY); + publicKeyFile = info.getProperty(KEY_FILE); + x509File = info.getProperty(X509_FILE); } - return this.publicKey; + if (publicKeyFile != null) { + if (log.isDebugEnabled()) { + log.debug("Decrypt by public key file"); + } + return getPublicKeyByPublicKeyFile(publicKeyFile); + } + + if (x509File != null) { + if (log.isDebugEnabled()) { + log.debug("Decrypt by X509 file"); + } + return getPublicKeyByX509(x509File); + } + + if (log.isDebugEnabled()) { + log.debug("Decrypt by public key string"); + } + + return getPublicKeyByString(key); } - public void setPublicKeyString(String publicKeyString) { - if (publicKeyString == null) { - throw new IllegalArgumentException("The publicKeyString cannot be null"); + PublicKey getPublicKeyByString(String publicKeyString) { + if (publicKeyString == null || publicKeyString.length() == 0) { + if (log.isDebugEnabled()) { + log.debug("Decrypt by default key"); + } + publicKeyString = DEFAULT_PUBLIC_KEY_STRING; } try { @@ -62,13 +114,17 @@ public class RsaDecrypter extends AbstractDecrypter { X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(publicKeyBytes); KeyFactory keyFactory = KeyFactory.getInstance("RSA"); - this.publicKey = keyFactory.generatePublic(x509KeySpec); + return keyFactory.generatePublic(x509KeySpec); } catch (Exception e) { throw new IllegalArgumentException("Failed to get public key", e); } } - public void setPublicKeyFile(String publicKeyFile) { + PublicKey getPublicKeyByPublicKeyFile(String publicKeyFile) { + if (publicKeyFile == null || publicKeyFile.length() == 0) { + return getPublicKeyByString(null); + } + FileInputStream in = null; try { in = new FileInputStream(publicKeyFile); @@ -82,36 +138,30 @@ public class RsaDecrypter extends AbstractDecrypter { byte[] publicKeyBytes = out.toByteArray(); X509EncodedKeySpec spec = new X509EncodedKeySpec(publicKeyBytes); KeyFactory factory = KeyFactory.getInstance("RSA"); - this.publicKey = factory.generatePublic(spec); + return factory.generatePublic(spec); } catch (Exception e) { throw new IllegalArgumentException("Failed to get public key", e); } finally { - if (in != null) { - try { - in.close(); - } catch (Exception e) {} - } + JdbcUtils.close(in); } } - public void setX509File(String x509File) { + PublicKey getPublicKeyByX509(String x509File) { + if (x509File == null || x509File.length() == 0) { + return getPublicKeyByString(null); + } + FileInputStream in = null; try { in = new FileInputStream(x509File); CertificateFactory factory = CertificateFactory.getInstance("X.509"); Certificate cer = factory.generateCertificate(in); - this.publicKey = cer.getPublicKey(); + return cer.getPublicKey(); } catch (Exception e) { throw new IllegalArgumentException("Failed to get public key", e); } finally { - if (in != null) { - try { - in.close(); - } catch (IOException e) { - log.warn("Cannot close file [" + x509File + "] in initialized public key."); - } - } + JdbcUtils.close(in); } } } diff --git a/src/main/java/com/alibaba/druid/pool/SensitiveParameters.java b/src/main/java/com/alibaba/druid/support/security/decryptor/SensitiveParameters.java similarity index 93% rename from src/main/java/com/alibaba/druid/pool/SensitiveParameters.java rename to src/main/java/com/alibaba/druid/support/security/decryptor/SensitiveParameters.java index 2532c09690..3d2ddbf3a7 100644 --- a/src/main/java/com/alibaba/druid/pool/SensitiveParameters.java +++ b/src/main/java/com/alibaba/druid/support/security/decryptor/SensitiveParameters.java @@ -1,4 +1,4 @@ -package com.alibaba.druid.pool; +package com.alibaba.druid.support.security.decryptor; /** *
diff --git a/src/main/resources/META-INF/druid-config-loader-default.properties b/src/main/resources/META-INF/druid-config-loader-default.properties
new file mode 100644
index 0000000000..0cf3be00df
--- /dev/null
+++ b/src/main/resources/META-INF/druid-config-loader-default.properties
@@ -0,0 +1,2 @@
+config.loader.1=com.alibaba.druid.filter.config.impl.FileConfigLoader
+config.loader.2=com.alibaba.druid.filter.config.impl.HttpConfigLoader
\ No newline at end of file
diff --git a/src/test/java/com/alibaba/druid/bvt/filter/config/ConfigLoadFilterTest.java b/src/test/java/com/alibaba/druid/bvt/filter/config/ConfigLoadFilterTest.java
deleted file mode 100644
index 513db3bdfd..0000000000
--- a/src/test/java/com/alibaba/druid/bvt/filter/config/ConfigLoadFilterTest.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright 1999-2011 Alibaba Group Holding Ltd.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.alibaba.druid.bvt.filter.config;
-
-import java.sql.Connection;
-
-import com.alibaba.druid.mock.MockDriver;
-import com.alibaba.druid.pool.DruidDataSource;
-import com.alibaba.druid.util.JdbcUtils;
-
-import junit.framework.Assert;
-import junit.framework.TestCase;
-
-public class ConfigLoadFilterTest extends TestCase {
-
-    private DruidDataSource dataSource;
-
-    protected void setUp() throws Exception {
-        dataSource = new DruidDataSource();
-        dataSource.setUrl("druid-configFile=bvt/config/config-0.properties");
-    }
-
-    protected void tearDown() throws Exception {
-        JdbcUtils.close(dataSource);
-    }
-
-    public void test_config() throws Exception {
-        Assert.assertEquals(true, dataSource.isTestOnBorrow()); // default
-        Assert.assertEquals(8, dataSource.getMaxActive());
-        
-        dataSource.init();
-
-        Assert.assertEquals(false, dataSource.isTestOnBorrow());
-        Assert.assertEquals(10, dataSource.getMaxActive());
-        Assert.assertEquals("jdbc:mock:config-0", dataSource.getUrl());
-        Assert.assertEquals(MockDriver.instance, dataSource.getDriver());
-
-        Connection conn = dataSource.getConnection();
-        conn.close();
-    }
-}
diff --git a/src/test/java/com/alibaba/druid/bvt/filter/config/ConfigLoadFilterTest1.java b/src/test/java/com/alibaba/druid/bvt/filter/config/ConfigLoadFilterTest1.java
deleted file mode 100644
index 455ad7f53a..0000000000
--- a/src/test/java/com/alibaba/druid/bvt/filter/config/ConfigLoadFilterTest1.java
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright 1999-2011 Alibaba Group Holding Ltd.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.alibaba.druid.bvt.filter.config;
-
-import java.sql.Connection;
-
-import junit.framework.Assert;
-import junit.framework.TestCase;
-
-import com.alibaba.druid.filter.config.ConfigFilter;
-import com.alibaba.druid.mock.MockDriver;
-import com.alibaba.druid.pool.DruidDataSource;
-import com.alibaba.druid.util.JdbcUtils;
-
-public class ConfigLoadFilterTest1 extends TestCase {
-
-    private DruidDataSource dataSource;
-    private ConfigFilter    configFilter;
-
-    protected void setUp() throws Exception {
-        configFilter = new ConfigFilter();
-        configFilter.setFile("bvt/config/config-1.properties");
-
-        dataSource = new DruidDataSource();
-        dataSource.getProxyFilters().add(configFilter);
-    }
-
-    protected void tearDown() throws Exception {
-        JdbcUtils.close(dataSource);
-    }
-
-    public void test_config() throws Exception {
-        Assert.assertEquals(true, dataSource.isTestOnBorrow()); // default
-        Assert.assertEquals(8, dataSource.getMaxActive());
-        
-        Assert.assertEquals(1, dataSource.getProxyFilters().size());
-        
-        dataSource.init();
-
-        Assert.assertEquals(false, dataSource.isTestOnBorrow());
-        Assert.assertEquals(11, dataSource.getMaxActive());
-        Assert.assertEquals("jdbc:mock:config-1", dataSource.getUrl());
-        Assert.assertEquals(MockDriver.instance, dataSource.getDriver());
-        Assert.assertEquals(3, dataSource.getProxyFilters().size());
-
-        Connection conn = dataSource.getConnection();
-        conn.close();
-    }
-}
diff --git a/src/test/java/com/alibaba/druid/bvt/filter/config/ConfigLoadFilterTest2.java b/src/test/java/com/alibaba/druid/bvt/filter/config/ConfigLoadFilterTest2.java
deleted file mode 100644
index fb311b412c..0000000000
--- a/src/test/java/com/alibaba/druid/bvt/filter/config/ConfigLoadFilterTest2.java
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Copyright 1999-2011 Alibaba Group Holding Ltd.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.alibaba.druid.bvt.filter.config;
-
-import java.sql.Connection;
-
-import junit.framework.Assert;
-import junit.framework.TestCase;
-
-import com.alibaba.druid.filter.config.ConfigFilter;
-import com.alibaba.druid.mock.MockDriver;
-import com.alibaba.druid.pool.DruidDataSource;
-import com.alibaba.druid.util.JdbcUtils;
-
-public class ConfigLoadFilterTest2 extends TestCase {
-
-    private DruidDataSource dataSource;
-    private ConfigFilter    configFilter;
-
-    protected void setUp() throws Exception {
-        configFilter = new ConfigFilter();
-        configFilter.setFile("bvt/config/config-2.properties");
-        configFilter.setKey("MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAMmVkLy+Cy/3rQ1OyH7nz1tT8Bu2KEU+z5LbeZ1yue/RN4KlcOEalj/K9Ev05Lpfu9cOdYnYM8ka1MiQdHLyJP0CAwEAAQ==");
-
-        dataSource = new DruidDataSource();
-        dataSource.getProxyFilters().add(configFilter);
-    }
-
-    protected void tearDown() throws Exception {
-        JdbcUtils.close(dataSource);
-    }
-
-    public void test_config() throws Exception {
-        Assert.assertEquals(true, dataSource.isTestOnBorrow()); // default
-        Assert.assertEquals(8, dataSource.getMaxActive());
-        
-        dataSource.init();
-
-        Assert.assertEquals(false, dataSource.isTestOnBorrow());
-        Assert.assertEquals(12, dataSource.getMaxActive());
-        Assert.assertEquals("jdbc:mock:config-2", dataSource.getUrl());
-        Assert.assertEquals(MockDriver.instance, dataSource.getDriver());
-        Assert.assertEquals("abc", dataSource.getPassword());
-
-        Connection conn = dataSource.getConnection();
-        conn.close();
-    }
-}
diff --git a/src/test/java/com/alibaba/druid/bvt/filter/config/ConfigLoadFilterTest3.java b/src/test/java/com/alibaba/druid/bvt/filter/config/ConfigLoadFilterTest3.java
deleted file mode 100644
index 39c6274f66..0000000000
--- a/src/test/java/com/alibaba/druid/bvt/filter/config/ConfigLoadFilterTest3.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Copyright 1999-2011 Alibaba Group Holding Ltd.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.alibaba.druid.bvt.filter.config;
-
-import java.sql.Connection;
-
-import com.alibaba.druid.filter.config.ConfigFilter;
-import com.alibaba.druid.mock.MockDriver;
-import com.alibaba.druid.pool.DruidDataSource;
-import com.alibaba.druid.util.JdbcUtils;
-
-import junit.framework.Assert;
-import junit.framework.TestCase;
-
-public class ConfigLoadFilterTest3 extends TestCase {
-    private DruidDataSource dataSource;
-    private ConfigFilter    configFilter;
-
-    protected void setUp() throws Exception {
-        configFilter = new ConfigFilter();
-        configFilter.setEncryptedPassword("TFxB6eJcgxE1hxOgiwOC/L7zWR/9vnSIfpggI2PTfcvvRhSnCGCPwI9n03fiJiLmRdnDU2/KaVTJYwz8zzkBqg==");
-        configFilter.setKey("MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAMmVkLy+Cy/3rQ1OyH7nz1tT8Bu2KEU+z5LbeZ1yue/RN4KlcOEalj/K9Ev05Lpfu9cOdYnYM8ka1MiQdHLyJP0CAwEAAQ==");
-
-        dataSource = new DruidDataSource();
-        dataSource.setUrl("jdbc:mock:config-3");
-        dataSource.setMaxActive(13);
-        dataSource.getProxyFilters().add(configFilter);
-        dataSource.setTestOnBorrow(false);
-    }
-    
-    protected void tearDown() throws Exception {
-        JdbcUtils.close(dataSource);
-    }
-    
-    public void test_config() throws Exception {
-        dataSource.init();
-
-        Assert.assertEquals(13, dataSource.getMaxActive());
-        Assert.assertEquals("jdbc:mock:config-3", dataSource.getUrl());
-        Assert.assertEquals(MockDriver.instance, dataSource.getDriver());
-        Assert.assertEquals("abc", dataSource.getPassword());
-
-        Connection conn = dataSource.getConnection();
-        conn.close();
-    }
-}
diff --git a/src/test/java/com/alibaba/druid/bvt/filter/config/ConfigLoadFilterTest4.java b/src/test/java/com/alibaba/druid/bvt/filter/config/ConfigLoadFilterTest4.java
deleted file mode 100644
index 721ef54eef..0000000000
--- a/src/test/java/com/alibaba/druid/bvt/filter/config/ConfigLoadFilterTest4.java
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright 1999-2011 Alibaba Group Holding Ltd.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.alibaba.druid.bvt.filter.config;
-
-import java.sql.Connection;
-
-import com.alibaba.druid.filter.config.ConfigFilter;
-import com.alibaba.druid.mock.MockDriver;
-import com.alibaba.druid.pool.DruidDataSource;
-import com.alibaba.druid.util.JdbcUtils;
-
-import junit.framework.Assert;
-import junit.framework.TestCase;
-
-public class ConfigLoadFilterTest4 extends TestCase {
-
-    private DruidDataSource dataSource;
-    private ConfigFilter    configFilter;
-
-    protected void setUp() throws Exception {
-        System.setProperty(ConfigFilter.SYS_PROP_CONFIG_KEY,
-                           "MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAMmVkLy+Cy/3rQ1OyH7nz1tT8Bu2KEU+z5LbeZ1yue/RN4KlcOEalj/K9Ev05Lpfu9cOdYnYM8ka1MiQdHLyJP0CAwEAAQ==");
-
-        configFilter = new ConfigFilter();
-        configFilter.setEncryptedPassword("TFxB6eJcgxE1hxOgiwOC/L7zWR/9vnSIfpggI2PTfcvvRhSnCGCPwI9n03fiJiLmRdnDU2/KaVTJYwz8zzkBqg==");
-        dataSource = new DruidDataSource();
-        dataSource.setUrl("jdbc:mock:config-3");
-        dataSource.setMaxActive(13);
-        dataSource.getProxyFilters().add(configFilter);
-        dataSource.setTestOnBorrow(false);
-    }
-
-    protected void tearDown() throws Exception {
-        JdbcUtils.close(dataSource);
-        System.clearProperty(ConfigFilter.SYS_PROP_CONFIG_KEY);
-    }
-
-    public void test_config() throws Exception {
-        dataSource.init();
-
-        Assert.assertEquals(13, dataSource.getMaxActive());
-        Assert.assertEquals("jdbc:mock:config-3", dataSource.getUrl());
-        Assert.assertEquals(MockDriver.instance, dataSource.getDriver());
-        Assert.assertEquals("abc", dataSource.getPassword());
-
-        Connection conn = dataSource.getConnection();
-        conn.close();
-    }
-}
diff --git a/src/test/java/com/alibaba/druid/bvt/filter/config/ConfigLoadFilterTest5.java b/src/test/java/com/alibaba/druid/bvt/filter/config/ConfigLoadFilterTest5.java
deleted file mode 100644
index 4450a74afe..0000000000
--- a/src/test/java/com/alibaba/druid/bvt/filter/config/ConfigLoadFilterTest5.java
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright 1999-2011 Alibaba Group Holding Ltd.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.alibaba.druid.bvt.filter.config;
-
-import java.sql.Connection;
-
-import com.alibaba.druid.filter.config.ConfigFilter;
-import com.alibaba.druid.mock.MockDriver;
-import com.alibaba.druid.pool.DruidDataSource;
-import com.alibaba.druid.util.JdbcUtils;
-
-import junit.framework.Assert;
-import junit.framework.TestCase;
-
-public class ConfigLoadFilterTest5 extends TestCase {
-
-    private DruidDataSource dataSource;
-
-    protected void setUp() throws Exception {
-        System.setProperty(ConfigFilter.SYS_PROP_CONFIG_KEY,
-                           "MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAMmVkLy+Cy/3rQ1OyH7nz1tT8Bu2KEU+z5LbeZ1yue/RN4KlcOEalj/K9Ev05Lpfu9cOdYnYM8ka1MiQdHLyJP0CAwEAAQ==");
-
-        System.setProperty(ConfigFilter.SYS_PROP_CONFIG_ENCRYPTED_PASSWORD,
-                           "TFxB6eJcgxE1hxOgiwOC/L7zWR/9vnSIfpggI2PTfcvvRhSnCGCPwI9n03fiJiLmRdnDU2/KaVTJYwz8zzkBqg==");
-        dataSource = new DruidDataSource();
-        dataSource.setUrl("jdbc:mock:config-3");
-        dataSource.setMaxActive(13);
-        dataSource.setFilters("config");
-        dataSource.setTestOnBorrow(false);
-    }
-
-    protected void tearDown() throws Exception {
-        JdbcUtils.close(dataSource);
-        System.clearProperty(ConfigFilter.SYS_PROP_CONFIG_KEY);
-        System.clearProperty(ConfigFilter.SYS_PROP_CONFIG_ENCRYPTED_PASSWORD);
-    }
-
-    public void test_config() throws Exception {
-        dataSource.init();
-
-        Assert.assertEquals(13, dataSource.getMaxActive());
-        Assert.assertEquals("jdbc:mock:config-3", dataSource.getUrl());
-        Assert.assertEquals(MockDriver.instance, dataSource.getDriver());
-        Assert.assertEquals("abc", dataSource.getPassword());
-
-        Connection conn = dataSource.getConnection();
-        conn.close();
-    }
-}
diff --git a/src/test/java/com/alibaba/druid/bvt/filter/config/ConfigLoadFilterTest6.java b/src/test/java/com/alibaba/druid/bvt/filter/config/ConfigLoadFilterTest6.java
deleted file mode 100644
index 3b4033535f..0000000000
--- a/src/test/java/com/alibaba/druid/bvt/filter/config/ConfigLoadFilterTest6.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright 1999-2011 Alibaba Group Holding Ltd.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.alibaba.druid.bvt.filter.config;
-
-import java.sql.Connection;
-
-import com.alibaba.druid.mock.MockDriver;
-import com.alibaba.druid.pool.DruidDataSource;
-import com.alibaba.druid.util.JdbcUtils;
-
-import junit.framework.Assert;
-import junit.framework.TestCase;
-
-public class ConfigLoadFilterTest6 extends TestCase {
-
-    private DruidDataSource dataSource;
-
-    protected void setUp() throws Exception {
-        dataSource = new DruidDataSource();
-        dataSource.setUrl("druid-configFile=https://raw.github.com/AlibabaTech/druid/master/src/test/resources/bvt/config/config-0.properties");
-    }
-
-    protected void tearDown() throws Exception {
-        JdbcUtils.close(dataSource);
-    }
-
-    public void test_http_config() throws Exception {
-        Assert.assertEquals(true, dataSource.isTestOnBorrow()); // default
-        Assert.assertEquals(8, dataSource.getMaxActive());
-        
-        dataSource.init();
-
-        Assert.assertEquals(false, dataSource.isTestOnBorrow());
-        Assert.assertEquals(10, dataSource.getMaxActive());
-        Assert.assertEquals("jdbc:mock:config-0", dataSource.getUrl());
-        Assert.assertEquals(MockDriver.instance, dataSource.getDriver());
-
-        Connection conn = dataSource.getConnection();
-        conn.close();
-    }
-}
diff --git a/src/test/java/com/alibaba/druid/bvt/filter/config/ConfigTestSuite.java b/src/test/java/com/alibaba/druid/bvt/filter/config/ConfigTestSuite.java
new file mode 100644
index 0000000000..d8ca9ce3d3
--- /dev/null
+++ b/src/test/java/com/alibaba/druid/bvt/filter/config/ConfigTestSuite.java
@@ -0,0 +1,26 @@
+package com.alibaba.druid.bvt.filter.config;
+
+import com.alibaba.druid.filter.config.ConfigFilterTest;
+import com.alibaba.druid.filter.config.ConfigLoaderFactoryTest;
+import com.alibaba.druid.filter.config.impl.FileConfigLoaderTest;
+import com.alibaba.druid.filter.config.impl.FileConfigLoaderTest1;
+import com.alibaba.druid.filter.config.impl.HttpConfigLoaderTest;
+import com.alibaba.druid.filter.config.impl.HttpConfigLoaderTest1;
+import org.junit.runner.RunWith;
+import org.junit.runners.Suite;
+
+/**
+ * @author Jonas Yang
+ */
+
+@RunWith(Suite.class)
+@Suite.SuiteClasses({
+        ConfigLoaderFactoryTest.class,
+        FileConfigLoaderTest.class,
+        FileConfigLoaderTest1.class,
+        HttpConfigLoaderTest.class,
+        HttpConfigLoaderTest1.class,
+        ConfigFilterTest.class
+})
+public class ConfigTestSuite {
+}
diff --git a/src/test/java/com/alibaba/druid/bvt/filter/config/ConfigToolTest.java b/src/test/java/com/alibaba/druid/bvt/filter/config/ConfigToolTest.java
deleted file mode 100644
index 0604016c9a..0000000000
--- a/src/test/java/com/alibaba/druid/bvt/filter/config/ConfigToolTest.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright 1999-2011 Alibaba Group Holding Ltd.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.alibaba.druid.bvt.filter.config;
-
-import java.util.Map;
-
-import junit.framework.Assert;
-import junit.framework.TestCase;
-
-import com.alibaba.druid.filter.config.ConfigTool;
-
-public class ConfigToolTest extends TestCase {
-
-    public void test_configTool() throws Exception {
-        Map result = ConfigTool.gen(1024);
-
-        String publicKey = result.get("publicKey");
-        String privateKey = result.get("privateKey");
-
-        System.out.println("publicKey : " + publicKey);
-        System.out.println("privateKey : " + privateKey);
-
-        String cipher = ConfigTool.encrypt(privateKey, "abcdef").get("result");
-        System.out.println("cipher : " + cipher);
-
-        String plain = ConfigTool.decrypt(publicKey, cipher).get("result");
-        System.out.println("plain : " + plain);
-        
-        Assert.assertEquals("abcdef", plain);
-    }
-}
diff --git a/src/test/java/com/alibaba/druid/bvt/pool/DruidDataSourceDecryptTest.java b/src/test/java/com/alibaba/druid/bvt/pool/DruidDataSourceDecryptTest.java
index a6452ca0f3..fca599c560 100644
--- a/src/test/java/com/alibaba/druid/bvt/pool/DruidDataSourceDecryptTest.java
+++ b/src/test/java/com/alibaba/druid/bvt/pool/DruidDataSourceDecryptTest.java
@@ -1,14 +1,15 @@
 package com.alibaba.druid.bvt.pool;
 
-import com.alibaba.druid.pool.DecryptException;
-import com.alibaba.druid.pool.Decrypter;
+import com.alibaba.druid.support.security.decryptor.DecryptException;
+import com.alibaba.druid.support.security.decryptor.Decrypter;
 import com.alibaba.druid.pool.DruidDataSource;
-import com.alibaba.druid.pool.SensitiveParameters;
+import com.alibaba.druid.support.security.decryptor.SensitiveParameters;
 import com.alibaba.druid.util.JdbcUtils;
 import org.junit.Assert;
 import org.junit.Test;
 
 import java.sql.SQLException;
+import java.util.Properties;
 
 /**
  * @author Jonas Yang
@@ -20,23 +21,13 @@ public class DruidDataSourceDecryptTest {
         DruidDataSource dataSource = new DruidDataSource();
         dataSource.setUrl("jdbc:oracle:thin:@");
         dataSource.setUsername("xiaoyu");
-        dataSource.setPassword("druid");
-
-        dataSource.setDecrypter(new Decrypter() {
-            @Override
-            public SensitiveParameters decrypt(SensitiveParameters parameters) throws DecryptException {
-                Assert.assertEquals("xiaoyu", parameters.getUsername());
-                throw new DecryptException("Test");
-            }
-        });
+        dataSource.setPassword("OJfUm6WCHi7EuXqE6aEc+Po2xFrAGBeSNy8O2jWhV2FTG8/5kbRRr2rjNKhptlevm/03Y0048P7h88gdUOXAYg==");
+        dataSource.setFilters("config");
+        dataSource.setConnectionProperties("config.decrypt=RSA");
 
         try {
             dataSource.init();
-            dataSource.createPhysicalConnection();
-            Assert.assertTrue("Got Here.", false);
-        } catch (SQLException e) {
-            Assert.assertEquals("The cause clas is " + e.getCause().getClass(), DecryptException.class, e.getCause().getClass());
-            Assert.assertEquals("The excepiton message is " + e.getCause().getMessage(), "Test", e.getCause().getMessage());
+            Assert.assertEquals("The password is " + dataSource.getPassword(), "xiaoyu", dataSource.getPassword());
         } finally {
             JdbcUtils.close(dataSource);
         }
diff --git a/src/test/java/com/alibaba/druid/bvt/support/security/DecrypterTestSuite.java b/src/test/java/com/alibaba/druid/bvt/support/security/DecrypterTestSuite.java
index 2cca39e5e9..ad935622bf 100644
--- a/src/test/java/com/alibaba/druid/bvt/support/security/DecrypterTestSuite.java
+++ b/src/test/java/com/alibaba/druid/bvt/support/security/DecrypterTestSuite.java
@@ -1,6 +1,7 @@
 package com.alibaba.druid.bvt.support.security;
 
 import com.alibaba.druid.support.security.decryptor.AesDecrypterTest;
+import com.alibaba.druid.support.security.decryptor.DecrypterFactoryTest;
 import com.alibaba.druid.support.security.decryptor.RsaDecrypterTest;
 import org.junit.runner.RunWith;
 import org.junit.runners.Suite;
@@ -11,7 +12,8 @@ import org.junit.runners.Suite;
 @RunWith(Suite.class)
 @Suite.SuiteClasses({
         AesDecrypterTest.class,
-        RsaDecrypterTest.class
+        RsaDecrypterTest.class,
+        DecrypterFactoryTest.class
 })
 public class DecrypterTestSuite {
 }
diff --git a/src/test/java/com/alibaba/druid/filter/config/ConfigFileGenerator.java b/src/test/java/com/alibaba/druid/filter/config/ConfigFileGenerator.java
new file mode 100644
index 0000000000..699a8f371a
--- /dev/null
+++ b/src/test/java/com/alibaba/druid/filter/config/ConfigFileGenerator.java
@@ -0,0 +1,55 @@
+package com.alibaba.druid.filter.config;
+
+import com.alibaba.druid.pool.DruidDataSourceFactory;
+import junit.framework.Assert;
+import org.junit.After;
+import org.junit.Before;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.PrintWriter;
+
+/**
+ * @author Jonas Yang
+ */
+public class ConfigFileGenerator {
+
+    protected String filePath;
+
+    @Before
+    public void setUp() {
+        PrintWriter out = null;
+
+        try {
+            File file = File.createTempFile("MyTest", Long.toString(System.nanoTime()));
+            filePath = file.getAbsolutePath();
+
+            out = new PrintWriter(new FileWriter(file));
+            out.println(DruidDataSourceFactory.PROP_USERNAME + "=test1");
+            out.println(DruidDataSourceFactory.PROP_PASSWORD + "=OJfUm6WCHi7EuXqE6aEc+Po2xFrAGBeSNy8O2jWhV2FTG8/5kbRRr2rjNKhptlevm/03Y0048P7h88gdUOXAYg==");
+            out.println(DruidDataSourceFactory.PROP_URL + "=jdbc:oracle:thin:@");
+        } catch (IOException e) {
+            Assert.assertNull("Failed to init resource.", e);
+        } finally {
+            if (out != null) {
+                try {
+                    out.close();
+                } catch(Exception e){}
+            }
+        }
+    }
+
+    @After
+    public void tearDown() {
+        if (this.filePath == null) {
+            return;
+        }
+
+        File file = new File(this.filePath);
+        if(file.exists()) {
+            file.delete();
+        }
+    }
+
+}
diff --git a/src/test/java/com/alibaba/druid/filter/config/ConfigFilterTest.java b/src/test/java/com/alibaba/druid/filter/config/ConfigFilterTest.java
new file mode 100644
index 0000000000..24959862f7
--- /dev/null
+++ b/src/test/java/com/alibaba/druid/filter/config/ConfigFilterTest.java
@@ -0,0 +1,131 @@
+package com.alibaba.druid.filter.config;
+
+import com.alibaba.druid.filter.config.impl.FileConfigLoader;
+import com.alibaba.druid.mock.MockDriver;
+import com.alibaba.druid.pool.DruidDataSource;
+import com.alibaba.druid.util.JdbcUtils;
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.sql.SQLException;
+
+/**
+ * @author Jonas Yang
+ */
+public class ConfigFilterTest extends ConfigFileGenerator {
+
+    String encryptedString = "OJfUm6WCHi7EuXqE6aEc+Po2xFrAGBeSNy8O2jWhV2FTG8/5kbRRr2rjNKhptlevm/03Y0048P7h88gdUOXAYg==";
+
+    @Test
+    public void testInitRemoteConfigFile() throws SQLException {
+        DruidDataSource dataSource = new DruidDataSource();
+        dataSource.setFilters("config");
+        dataSource.setConnectionProperties("config.file=" + FileConfigLoader.PROTOCOL_PREFIX + this.filePath);
+        try {
+            dataSource.init();
+
+            Assert.assertEquals("The username is " + dataSource.getUsername(), "test1", dataSource.getUsername());
+        } finally {
+            JdbcUtils.close(dataSource);
+        }
+    }
+
+
+    @Test
+    public void testInitInvalidRemoteConfigFile() throws SQLException {
+        DruidDataSource dataSource = new DruidDataSource();
+        dataSource.setFilters("config");
+        dataSource.setConnectionProperties("config.file=abcdef");
+        try {
+            dataSource.init();
+            Assert.assertTrue("It is here", false);
+        } catch (IllegalArgumentException e) {
+            e.printStackTrace();
+            Assert.assertTrue(true);
+        } finally {
+            JdbcUtils.close(dataSource);
+        }
+    }
+
+    @Test
+    public void testInitDecrypt() throws SQLException {
+        DruidDataSource dataSource = new DruidDataSource();
+        dataSource.setDriver(MockDriver.instance);
+        dataSource.setUrl("");
+        dataSource.setUsername("test");
+        dataSource.setPassword(encryptedString);
+        dataSource.setFilters("config");
+        dataSource.setConnectionProperties("config.decrypt=RSA");
+
+        try {
+            dataSource.init();
+            Assert.assertEquals("The password is " + dataSource.getPassword() + ", is not xiaoyu", "xiaoyu", dataSource.getPassword());
+        } finally {
+            JdbcUtils.close(dataSource);
+        }
+    }
+
+    @Test
+    public void testInitInvalidDecrypt() throws SQLException {
+        DruidDataSource dataSource = new DruidDataSource();
+        dataSource.setDriver(MockDriver.instance);
+        dataSource.setUrl("");
+        dataSource.setUsername("test");
+        dataSource.setPassword(encryptedString);
+        dataSource.setFilters("config");
+        dataSource.setConnectionProperties("config.decrypt=ABCDEF");
+
+        try {
+            dataSource.init();
+            Assert.assertTrue("It is here", false);
+        } catch (IllegalArgumentException e) {
+            e.printStackTrace();
+            Assert.assertTrue(true);
+        } finally {
+            JdbcUtils.close(dataSource);
+        }
+    }
+
+    @Test
+    public void testInitRemoteConfigAndDecrypt() throws SQLException {
+        DruidDataSource dataSource = new DruidDataSource();
+        dataSource.setFilters("config");
+        dataSource.setConnectionProperties("config.decrypt=RSA;config.file=" + FileConfigLoader.PROTOCOL_PREFIX + this.filePath);
+        try {
+            dataSource.init();
+
+            Assert.assertEquals("The password is " + dataSource.getPassword(), "xiaoyu", dataSource.getPassword());
+        } finally {
+            JdbcUtils.close(dataSource);
+        }
+    }
+
+    @Test
+    public void testNormalInit() throws SQLException {
+        DruidDataSource dataSource = new DruidDataSource();
+        dataSource.setUrl("jdbc:oracle:thin:@");
+
+        try {
+            dataSource.init();
+        } finally {
+            JdbcUtils.close(dataSource);
+        }
+    }
+
+    @Test
+    public void testInvalidInit() throws SQLException {
+        DruidDataSource dataSource = new DruidDataSource();
+        dataSource.setDriver(MockDriver.instance);
+        dataSource.setFilters("config");
+        dataSource.setConnectionProperties("config.file=abcdefeg");
+
+        try {
+            dataSource.init();
+            Assert.assertTrue("Get here.", false);
+        } catch (IllegalArgumentException e) {
+            e.printStackTrace();
+        } finally {
+            JdbcUtils.close(dataSource);
+        }
+    }
+}
diff --git a/src/test/java/com/alibaba/druid/filter/config/ConfigLoaderFactoryTest.java b/src/test/java/com/alibaba/druid/filter/config/ConfigLoaderFactoryTest.java
new file mode 100644
index 0000000000..a53087f971
--- /dev/null
+++ b/src/test/java/com/alibaba/druid/filter/config/ConfigLoaderFactoryTest.java
@@ -0,0 +1,55 @@
+package com.alibaba.druid.filter.config;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.util.Properties;
+
+/**
+ * @author Jonas Yang
+ */
+public class ConfigLoaderFactoryTest {
+
+    @Test
+    public void testInitConfigLoader() {
+        ConfigLoader[] configLoaders = ConfigLoaderFactory.getConfigLoaders();
+        Assert.assertTrue("The size is " + configLoaders.length + " , not 2", configLoaders.length == 2);
+    }
+
+    @Test
+    public void testGetConfigLoader() {
+        ConfigLoader configLoader = ConfigLoaderFactory.getConfigLoader("abcde");
+        Assert.assertEquals("The config is " + configLoader, null, configLoader);
+    }
+
+    @Test
+    public void testAddConfigLoader() {
+        int size = ConfigLoaderFactory.getConfigLoaders().length;
+
+        ConfigLoaderFactory.addConfigLoader(new ConfigLoader() {
+            @Override
+            public String getId() {
+                return "My Test";  //To change body of implemented methods use File | Settings | File Templates.
+            }
+
+            @Override
+            public Properties loadConfig(String protocol) {
+                return null;  //To change body of implemented methods use File | Settings | File Templates.
+            }
+
+            @Override
+            public boolean isSupported(String protocol) {
+                if (protocol.startsWith("abc")) {
+                    return true;
+                }
+                return false;  //To change body of implemented methods use File | Settings | File Templates.
+            }
+        });
+
+        Assert.assertEquals("The size is " + ConfigLoaderFactory.getConfigLoaders().length, ConfigLoaderFactory.getConfigLoaders().length, size + 1);
+
+        ConfigLoader configLoader = ConfigLoaderFactory.getConfigLoader("abc");
+        Assert.assertNotNull("The config loader is null", configLoader);
+        Assert.assertEquals("The config loader's id is " + configLoader.getId(), "My Test", configLoader.getId());
+    }
+}
diff --git a/src/test/java/com/alibaba/druid/filter/config/impl/FileConfigLoaderTest.java b/src/test/java/com/alibaba/druid/filter/config/impl/FileConfigLoaderTest.java
new file mode 100644
index 0000000000..ad3298449a
--- /dev/null
+++ b/src/test/java/com/alibaba/druid/filter/config/impl/FileConfigLoaderTest.java
@@ -0,0 +1,35 @@
+package com.alibaba.druid.filter.config.impl;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * @author Jonas Yang
+ */
+public class FileConfigLoaderTest {
+
+    @Test
+    public void testIsSupported() {
+        FileConfigLoader configLoader = new FileConfigLoader();
+        boolean isSupported = configLoader.isSupported(FileConfigLoader.PROTOCOL_PREFIX + "/opt/mytest");
+        Assert.assertTrue("The value is " + isSupported + ", not true", isSupported);
+
+        isSupported = configLoader.isSupported("jdbc:/opt/mytest");
+        Assert.assertFalse("The value is " + isSupported + ", not false", isSupported);
+
+        System.setProperty(FileConfigLoader.SYS_PROP_CONFIG_FILE, "/opt/mytest");
+        isSupported = configLoader.isSupported(null);
+        Assert.assertTrue("The value is " + isSupported + ", not true", isSupported);
+    }
+
+    @Test
+    public void testGetFilePath() {
+        FileConfigLoader configLoader = new FileConfigLoader();
+        String filePath = configLoader.getFilePath(FileConfigLoader.PROTOCOL_PREFIX + "/opt/test/test.properties");
+        Assert.assertEquals("The path is " + filePath, filePath, "/opt/test/test.properties");
+
+        System.setProperty(FileConfigLoader.SYS_PROP_CONFIG_FILE, "/opt/mytest");
+        filePath = configLoader.getFilePath(null);
+        Assert.assertEquals("The path is " + filePath, filePath, "/opt/mytest");
+    }
+}
diff --git a/src/test/java/com/alibaba/druid/filter/config/impl/FileConfigLoaderTest1.java b/src/test/java/com/alibaba/druid/filter/config/impl/FileConfigLoaderTest1.java
new file mode 100644
index 0000000000..29ceb59a16
--- /dev/null
+++ b/src/test/java/com/alibaba/druid/filter/config/impl/FileConfigLoaderTest1.java
@@ -0,0 +1,42 @@
+package com.alibaba.druid.filter.config.impl;
+
+import com.alibaba.druid.filter.config.ConfigFileGenerator;
+import junit.framework.Assert;
+import org.junit.Test;
+
+import java.util.Properties;
+
+/**
+ * @author Jonas Yang
+ */
+public class FileConfigLoaderTest1 extends ConfigFileGenerator {
+
+    @Test
+    public void testLoadConfigByFile() {
+        FileConfigLoader configLoader = new FileConfigLoader();
+        Properties p = configLoader.loadConfig(FileConfigLoader.PROTOCOL_PREFIX + this.filePath);
+
+        Assert.assertNotNull("The properties is null", p);
+        Assert.assertEquals("The value is " + p.getProperty("username") + ", not test1", "test1", p.getProperty("username"));
+    }
+
+    @Test
+    public void testLoadConfigBySystemProperty() {
+        System.setProperty(FileConfigLoader.SYS_PROP_CONFIG_FILE, this.filePath);
+        FileConfigLoader configLoader = new FileConfigLoader();
+        Properties p = configLoader.loadConfig(null);
+
+        Assert.assertNotNull("The properties is null", p);
+        Assert.assertEquals("The value is " + p.getProperty("username") + ", not test1", "test1", p.getProperty("username"));
+
+    }
+
+    @Test
+    public void testLoadConfigByNotExistFile() {
+        FileConfigLoader configLoader = new FileConfigLoader();
+        Properties p = configLoader.loadConfig(FileConfigLoader.PROTOCOL_PREFIX + "/test/test/test");
+
+        Assert.assertNull("The properties is not null", p);
+    }
+
+}
diff --git a/src/test/java/com/alibaba/druid/filter/config/impl/HttpConfigLoaderTest.java b/src/test/java/com/alibaba/druid/filter/config/impl/HttpConfigLoaderTest.java
new file mode 100644
index 0000000000..1c176c2728
--- /dev/null
+++ b/src/test/java/com/alibaba/druid/filter/config/impl/HttpConfigLoaderTest.java
@@ -0,0 +1,31 @@
+package com.alibaba.druid.filter.config.impl;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+
+/**
+ * @author Jonas Yang
+ */
+public class HttpConfigLoaderTest {
+
+    @Test
+    public void testIsSupported() {
+        HttpConfigLoader configLoader = new HttpConfigLoader();
+        boolean isSupported = configLoader.isSupported(HttpConfigLoader.PROTOCOL_PREFIX + "www.5-nb.com");
+        Assert.assertTrue("The value is " + isSupported + ", not true", isSupported);
+
+        isSupported = configLoader.isSupported("jdbc:/opt/mytest");
+        Assert.assertFalse("The value is " + isSupported + ", not false", isSupported);
+    }
+
+    @Test
+    public void testGetUrl() throws MalformedURLException {
+        String protocol = HttpConfigLoader.PROTOCOL_PREFIX + "www.5-nb.com";
+        HttpConfigLoader configLoader = new HttpConfigLoader();
+        URL url = configLoader.getUrl(protocol);
+        Assert.assertEquals("The url is " + url, "http://www.5-nb.com", url.toString());
+    }
+}
diff --git a/src/test/java/com/alibaba/druid/filter/config/impl/HttpConfigLoaderTest1.java b/src/test/java/com/alibaba/druid/filter/config/impl/HttpConfigLoaderTest1.java
new file mode 100644
index 0000000000..9bd5ac2537
--- /dev/null
+++ b/src/test/java/com/alibaba/druid/filter/config/impl/HttpConfigLoaderTest1.java
@@ -0,0 +1,99 @@
+package com.alibaba.druid.filter.config.impl;
+
+import junit.framework.Assert;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.util.Properties;
+
+/**
+ * @author Jonas Yang
+ */
+public class HttpConfigLoaderTest1 {
+    protected String protocol = "localhost:19999/test.properties";
+
+    protected ServerSocket server;
+
+    @Before
+    public void setUp() {
+        try {
+            this.server = new ServerSocket(19999);
+            this.server.setReuseAddress(true);
+            Thread t = new Thread("Http Server Thread") {
+                @Override
+                public void run() {
+                    try {
+                        Socket socket = HttpConfigLoaderTest1.this.server.accept();
+//                            InputStream in = socket.getInputStream();
+//                            int b;
+//                            while ((b = in.read()) != -1) {
+//                                if (b == '\r') {
+//                                    b = in.read();
+//                                    if (b == '\n') {
+//                                        b = in.read();
+//                                        if (b == '\r') {
+//                                            b = in.read();
+//                                            if (b == '\n') {
+//                                                break;
+//                                            }
+//                                        }
+//                                    }
+//                                }
+//
+//                                if (b == -1) {
+//                                    break;
+//                                }
+//                            }
+
+                        PrintWriter out = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()));
+                        out.println("a1=test1");
+                        out.println("b2=test2");
+                        out.flush();
+                        socket.close();
+                    } catch (IOException e) {
+                        if (e.getMessage().indexOf("Socket closed") == -1
+                                && e.getMessage().indexOf("Socket is closed") == -1)
+                            e.printStackTrace();
+                    }
+                }
+            };
+            t.start();
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+    }
+
+    @After
+    public void tearDown() {
+        if (this.server != null) {
+            try {
+                this.server.close();
+            } catch (IOException e) {
+                e.printStackTrace();
+            }
+        }
+    }
+
+    @Test
+    public void testLoadConfig() {
+        HttpConfigLoader configLoader = new HttpConfigLoader();
+        Properties p = configLoader.loadConfig(HttpConfigLoader.PROTOCOL_PREFIX + this.protocol);
+
+        Assert.assertNotNull("The properties is null", p);
+        Assert.assertEquals("The value is " + p.getProperty("a1") + ", not test1", "test1", p.getProperty("a1"));
+    }
+
+    @Test
+    public void testLoadConfigByNotExistUrl() {
+        HttpConfigLoader configLoader = new HttpConfigLoader();
+        Properties p = configLoader.loadConfig(HttpConfigLoader.PROTOCOL_PREFIX + "localhost/jonas");
+
+        Assert.assertNull("The properties is not null", p);
+    }
+}
diff --git a/src/test/java/com/alibaba/druid/support/security/decryptor/AesDecrypterTest.java b/src/test/java/com/alibaba/druid/support/security/decryptor/AesDecrypterTest.java
index 0d5b9360d5..f0b7a7fa89 100644
--- a/src/test/java/com/alibaba/druid/support/security/decryptor/AesDecrypterTest.java
+++ b/src/test/java/com/alibaba/druid/support/security/decryptor/AesDecrypterTest.java
@@ -1,10 +1,10 @@
 package com.alibaba.druid.support.security.decryptor;
 
-import com.alibaba.druid.pool.DecryptException;
-import com.alibaba.druid.pool.SensitiveParameters;
 import junit.framework.Assert;
 import org.junit.Test;
 
+import java.util.Properties;
+
 /**
  * @author Jonas Yang
  */
@@ -13,8 +13,9 @@ public class AesDecrypterTest {
     @Test
     public void testDecrypt() throws DecryptException {
         AesDecrypter decrypter = new AesDecrypter();
-        decrypter.setKey("123456");
-        SensitiveParameters parameters = decrypter.decrypt(new SensitiveParameters("", "", "0UNXTyp/r/wdm+b5FAY1Jw=="));
+        Properties info = new Properties();
+        info.setProperty(AesDecrypter.KEY, "123456");
+        SensitiveParameters parameters = decrypter.decrypt(new SensitiveParameters("", "", "0UNXTyp/r/wdm+b5FAY1Jw=="), info);
 
         Assert.assertEquals("It is not same.", "xiaoyu", parameters.getPassword());
     }
@@ -22,7 +23,7 @@ public class AesDecrypterTest {
     @Test
     public void testDecryptWithEmptyKey() throws DecryptException {
         AesDecrypter decrypter = new AesDecrypter();
-        SensitiveParameters parameters = decrypter.decrypt(new SensitiveParameters("", "", "AzgdbVU3SIK6A8+0qg+Btg=="));
+        SensitiveParameters parameters = decrypter.decrypt(new SensitiveParameters("", "", "AzgdbVU3SIK6A8+0qg+Btg=="), null);
 
         Assert.assertEquals("It is not same.", "xiaoyu", parameters.getPassword());
     }
diff --git a/src/test/java/com/alibaba/druid/support/security/decryptor/DecrypterFactoryTest.java b/src/test/java/com/alibaba/druid/support/security/decryptor/DecrypterFactoryTest.java
new file mode 100644
index 0000000000..61a952fcdc
--- /dev/null
+++ b/src/test/java/com/alibaba/druid/support/security/decryptor/DecrypterFactoryTest.java
@@ -0,0 +1,21 @@
+package com.alibaba.druid.support.security.decryptor;
+
+import junit.framework.Assert;
+import org.junit.Test;
+
+/**
+ * @author Jonas Yang
+ */
+public class DecrypterFactoryTest {
+
+    @Test
+    public void testGetDecrypter() throws DecryptException {
+        Assert.assertNotNull(DecrypterFactory.getDecrypter("RSA"));
+        Assert.assertEquals(DecrypterFactory.getDecrypter("RSA").getClass(), RsaDecrypter.class);
+
+        Assert.assertNotNull(DecrypterFactory.getDecrypter("AES"));
+        Assert.assertEquals(DecrypterFactory.getDecrypter("AES").getClass(), AesDecrypter.class);
+
+        Assert.assertNull(DecrypterFactory.getDecrypter("ajfdjksfklds;"));
+    }
+}
diff --git a/src/test/java/com/alibaba/druid/support/security/decryptor/RsaDecrypterTest.java b/src/test/java/com/alibaba/druid/support/security/decryptor/RsaDecrypterTest.java
index 03fefc4c6b..2c0f356fa5 100644
--- a/src/test/java/com/alibaba/druid/support/security/decryptor/RsaDecrypterTest.java
+++ b/src/test/java/com/alibaba/druid/support/security/decryptor/RsaDecrypterTest.java
@@ -1,7 +1,5 @@
 package com.alibaba.druid.support.security.decryptor;
 
-import com.alibaba.druid.pool.DecryptException;
-import com.alibaba.druid.pool.SensitiveParameters;
 import com.alibaba.druid.util.Base64;
 import junit.framework.Assert;
 import org.junit.After;
@@ -9,6 +7,8 @@ import org.junit.Before;
 import org.junit.Test;
 
 import java.io.*;
+import java.security.PublicKey;
+import java.util.Properties;
 
 /**
  * @author Jonas Yang
@@ -52,11 +52,71 @@ public class RsaDecrypterTest {
         this.keyFilePath = null;
     }
 
+    @Test
+    public void testGetPublicKeyByString() {
+        RsaDecrypter rsaDecrypter = new RsaDecrypter();
+        PublicKey key = rsaDecrypter.getPublicKeyByString(this.publicKeyContent);
+        Assert.assertNotNull("It is null", key);
+
+        key = rsaDecrypter.getPublicKeyByString(null);
+        Assert.assertNotNull("It is null", key);
+        Assert.assertEquals(RsaDecrypter.DEFAULT_PUBLIC_KEY_STRING, Base64.byteArrayToBase64(key.getEncoded()));
+
+        try {
+            key = rsaDecrypter.getPublicKeyByString("abcdef");
+            Assert.assertTrue("It is here", false);
+        } catch (Exception e) {
+        }
+    }
+
+    @Test
+    public void testGetPublicKeyByPublicKeyFile() {
+        RsaDecrypter rsaDecrypter = new RsaDecrypter();
+        PublicKey key = rsaDecrypter.getPublicKeyByPublicKeyFile(this.keyFilePath);
+        Assert.assertNotNull("It is null", key);
+
+        key = rsaDecrypter.getPublicKeyByPublicKeyFile(null);
+        Assert.assertNotNull("It is null", key);
+        Assert.assertEquals(RsaDecrypter.DEFAULT_PUBLIC_KEY_STRING, Base64.byteArrayToBase64(key.getEncoded()));
+
+        try {
+            key = rsaDecrypter.getPublicKeyByPublicKeyFile("abcdef");
+            Assert.assertTrue("It is here", false);
+        } catch (Exception e) {
+        }
+    }
+
+    @Test
+    public void testGetPublicKeyByX509File() {
+        //No test
+    }
+
+    @Test
+    public void testGetPublicKey() {
+        RsaDecrypter rsaDecrypter = new RsaDecrypter();
+        PublicKey key = rsaDecrypter.getPublicKey(null);
+        Assert.assertNotNull("It is null", key);
+        Assert.assertEquals(RsaDecrypter.DEFAULT_PUBLIC_KEY_STRING, Base64.byteArrayToBase64(key.getEncoded()));
+
+        Properties info = new Properties();
+        info.put(RsaDecrypter.KEY, this.publicKeyContent);
+        key = rsaDecrypter.getPublicKey(info);
+        Assert.assertNotNull("It is null", key);
+        Assert.assertEquals(this.publicKeyContent, Base64.byteArrayToBase64(key.getEncoded()));
+
+        info = new Properties();
+        info.put(RsaDecrypter.KEY_FILE, this.keyFilePath);
+        key = rsaDecrypter.getPublicKey(info);
+        Assert.assertNotNull("It is null", key);
+        Assert.assertEquals(this.publicKeyContent, Base64.byteArrayToBase64(key.getEncoded()));
+    }
+
     @Test
     public void testDecrypt() throws DecryptException {
         RsaDecrypter decrypter = new RsaDecrypter();
-        decrypter.setPublicKeyFile(this.keyFilePath);
-        SensitiveParameters parameters = decrypter.decrypt(new SensitiveParameters("", "", encryptedString));
+        Properties info = new Properties();
+        info.put(RsaDecrypter.KEY_FILE, this.keyFilePath);
+        SensitiveParameters parameters = decrypter.decrypt(new SensitiveParameters("", "", encryptedString), info);
 
         Assert.assertEquals("It is not same.", "xiaoyu", parameters.getPassword());
     }
@@ -64,7 +124,7 @@ public class RsaDecrypterTest {
     @Test
     public void testDecryptWithEmptyKey() throws DecryptException {
         RsaDecrypter decrypter = new RsaDecrypter();
-        SensitiveParameters parameters = decrypter.decrypt(new SensitiveParameters("", "", "OJfUm6WCHi7EuXqE6aEc+Po2xFrAGBeSNy8O2jWhV2FTG8/5kbRRr2rjNKhptlevm/03Y0048P7h88gdUOXAYg=="));
+        SensitiveParameters parameters = decrypter.decrypt(new SensitiveParameters("", "", "OJfUm6WCHi7EuXqE6aEc+Po2xFrAGBeSNy8O2jWhV2FTG8/5kbRRr2rjNKhptlevm/03Y0048P7h88gdUOXAYg=="), null);
 
         Assert.assertEquals("It is not same.", "xiaoyu", parameters.getPassword());
     }