一、阿波罗使用Jasypt加密数据- 集成 步骤
1、添加依赖 (注意springboot 低于2.0版本, 使用响应的版本)
2、配置加密秘钥,三种方式
2.1 、application.properties 中添加属性文件
jasypt.encryptor.password=TESTaiunlv85I3p1nXI41JMYoaxBAKK1
但是这种方式硬编码了,不利于项目发布,环境切换
2.2、使用阿波罗配置几种环境,但是这种不方便encrypt 、decrypt 接口利用加密秘钥 生成不同环境的加密字符 , 因为不同环境,有可能项目启动不起来。
针对不同环境可以配置不同的encrypt password 例:
开发环境:
DEVTaiunlv85I3p1nXI41JMYoaxBAKK1
测试环境:
TESTaiunlv85I3p1nXI41JMYoaxBAKK1
生产环境:
PRODbiunlv85I3p1nXI41JMYoaxBAKK1
2.3、使用启动参数,使用这种方式,application.properties 不需要配置jasypt.encryptor.password 属性 , 否则会报错org.jasypt.exceptions.EncryptionOperationNotPossibleException
-Djasypt.encryptor.password=TESTaiunlv85I3p1nXI41JMYoaxBAKK1
动态获取加密秘钥(几种环境配置几种加密秘钥) ,encrypt 、 decrypt 等接口来生成加密字符, 再把加密字符配置到阿波罗属性中,以达到加密效果。
3、话不多说,直接上代码(这里为了多次生成加密后的字符串,写了一个controller,想要加密什么字符串直接用浏览器请求即可)
import lombok.extern.slf4j.Slf4j;
import org.jasypt.encryption.StringEncryptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
@Slf4j
public class JasyptToolController {
@Autowired
private StringEncryptor stringEncryptor;
private static final String ENCRYPTED_VALUE_PREFIX = "ENC(";
private static final String ENCRYPTED_VALUE_SUFFIX = ")";
public static boolean isEncryptedValue(final String value) {
if (value == null) {
return false;
}
final String trimmedValue = value.trim();
return (trimmedValue.startsWith(ENCRYPTED_VALUE_PREFIX) && trimmedValue.endsWith(ENCRYPTED_VALUE_SUFFIX));
}
private static String getInnerEncryptedValue(final String value) {
return value.substring( ENCRYPTED_VALUE_PREFIX.length(), (value.length() - ENCRYPTED_VALUE_SUFFIX.length()));
}
@RequestMapping(value = "/encrypt", method = RequestMethod.POST)
public
@ResponseBody
String encrypt( @RequestParam("text") String text) {
String encrypted = stringEncryptor.encrypt(text.trim());
log.info("原文: " + text);
log.info("加密: " + encrypted);
log.info("解密: " + stringEncryptor.decrypt(encrypted));
log.info("最终需要的加密串: " + String.format("ENC(%s)", encrypted) );//将最终加密串配置到apollo配置中心
return String.format("ENC(%s)", encrypted);
}
@RequestMapping(value = "/decrypt", method = RequestMethod.POST)
public
@ResponseBody
String decrypt( @RequestParam("text") String text) {
String decrypted = stringEncryptor.decrypt(isEncryptedValue(text) ? getInnerEncryptedValue(text) : text);
log.info("原加密: " + text);
log.info("解密: " + decrypted);
log.info("再次加密: " + String.format("ENC(%s)", stringEncryptor.encrypt(decrypted)));//将最终加密串配置到apollo配置中心
return decrypted;
}
}
二、spring boot使用jasypt加密原理解析
具体的解密过程
当spring boot项目启动的时候,需要用到属性值的时候,就是将原本spring中的propertySource的getProperty()方法委托给其自定义的实现EncryptablePropertySourceWrapper,调用其getProperty()方法,在这个方法的自定义实现中。判断是否是已经加密的value,如果是,则进行解密。如果不是,那就返回原值。
源码中主要的几个方法:
调用EncryptablePropertySourceWrapper的getProperty方法,其extends PropertySource,override了getProperty方法
public class EncryptablePropertySourceWrapper extends PropertySource implements EncryptablePropertySource {
private final PropertySource delegate;
private final StringEncryptor encryptor;
public EncryptablePropertySourceWrapper(PropertySource delegate, StringEncryptor encryptor) {
super(delegate.getName(), delegate.getSource());
Assert.notNull(delegate, "PropertySource delegate cannot be null");
Assert.notNull(encryptor, "StringEncryptor cannot be null");
this.delegate = delegate;
this.encryptor = encryptor;
}
@Override
public Object getProperty(String name) {
return getProperty(encryptor, delegate, name);
}
}
其getProperty就去调用其implements的EncryptablePropertySource的getProperty方法,于是执行下面
public interface EncryptablePropertySource {
public default Object getProperty(StringEncryptor encryptor, PropertySource source, String name) {
Object value = source.getProperty(name);
if(value instanceof String) {
String stringValue = String.valueOf(value);
if(PropertyValueEncryptionUtils.isEncryptedValue(stringValue)) {
value = PropertyValueEncryptionUtils.decrypt(stringValue, encryptor);
}
}
return value;
}
}
isEncryptedValue方法
private static final String ENCRYPTED_VALUE_PREFIX = “ENC(”;
private static final String ENCRYPTED_VALUE_SUFFIX = “)”;
public static boolean isEncryptedValue(final String value) {
if (value == null) {
return false;
}
final String trimmedValue = value.trim();
return (trimmedValue.startsWith(ENCRYPTED_VALUE_PREFIX) && trimmedValue.endsWith(ENCRYPTED_VALUE_SUFFIX));
}
如果通过 prefixes/suffixes 包裹的属性,那么返回解密后的值;
如果没有被包裹,那么返回原生的值;