jasypt简介:Jasypt是一个Java库,允许开发人员以最小的努力将基本的加密功能添加到项目中,而无需深入了解加密协议的实现细节。
1.目的
对数据库存储的敏感信息,如身份证号、电话号码等进行加解密处理;
对配置文件中的敏感信息,如jdbc账号密码进行加密处理。
2.引入配置
第一种方式前提是持久层采用mybatis框架,如采用其他持久层框架可采用第二种注解方式
2.1 引用jar包
目前通用版本为2.x和3.x 区别
2.x
- 所依赖的springboot版本为2.0.0~3.1.0
- 默认加密算法 PBEWithMD5AndDES
- 默认生成盐的类org.jasypt.salt.RandomSaltGenerator
- 默认IV生成器类 org.jasypt.iv.NoIvGenerator
3.x
- 所依赖的springboot版本为2.2.1~3.1.0
- 默认加密算法 PBEWITHHMACSHA512ANDAES_256
- 默认生成盐的类org.jasypt.salt.RandomSaltGenerator
- 默认IV生成器类org.jasypt.iv.RandomIvGenerator
<dependency>
<groupId>com.github.ulisesbocchio</groupId>
<artifactId>jasypt-spring-boot-starter</artifactId>
<version>3.0.4</version>
</dependency>
2.2 yml文件配置相关属性
jasypt:
encryptor:
# 盐值 需要配置
password: salt123
# 指定加密方式 (可以不配置有默认值)
algorithm: PBEWITHHMACSHA512ANDAES_256
#设置初始向量IV生成器的类名 (可以不配置有默认值)
iv-generator-classname: org.jasypt.iv.NoIvGenerator
property: #(可以不配置 默认为ENC() )
# 标识为加密属性的前缀
prefix: ENC(
# 标识为加密属性的后缀
suffix: )
3.操作实现(第一种)(相关代码 复制粘贴即可)
第一种方式采用mybatis的类型转换器(TypeHandler)
注意:采用mybatis自带的方法可以直接使用,在属性字段上加入
@TableField(typeHandler = JasyptMybatisHandler.class)即可
mybatis-plues 则需在实体类上加@TableName(autoResultMap = true) 开启映射注解
@Component
public class JasyptMybatisHandler implements TypeHandler<String> {
private final StringEncryptor encryptor;
public JasyptMybatisHandler(StringEncryptor encryptor) {
this.encryptor = encryptor;
}
@Override
public void setParameter(PreparedStatement preparedStatement, int i, String s, JdbcType jdbcType) throws SQLException {
if (StringUtils.isEmpty(s)) {
preparedStatement.setString(i, "");
} else {
preparedStatement.setString(i, encryptor.encrypt(s.trim()));
}
}
@Override
public String getResult(ResultSet resultSet, String s) throws SQLException {
if (StringUtils.isEmpty(resultSet.getString(s))) {
return resultSet.getString(s);
} else {
return encryptor.decrypt(resultSet.getString(s).trim());
}
}
@Override
public String getResult(ResultSet resultSet, int i) throws SQLException {
if (StringUtils.isEmpty(resultSet.getString(i))) {
return resultSet.getString(i);
} else {
return encryptor.decrypt(resultSet.getString(i).trim());
}
}
@Override
public String getResult(CallableStatement callableStatement, int i) throws SQLException {
if (StringUtils.isEmpty(callableStatement.getString(i))) {
return callableStatement.getString(i);
} else {
return encryptor.decrypt(callableStatement.getString(i).trim());
}
}
}
4.操作实现 自定义注解
加解密方法注解
@Documented
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface JasyptMethod {
/**
* 加密enc 解密dec
*/
String type() default "encrypt";
}
加解密字段注解
@Documented
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface JasyptField {
}
加解密切面 可以自己在优化优化
@Aspect
@Component
@Slf4j
public class JasyptAspect {
private final StringEncryptor stringEncryptor;
public JasyptAspect(StringEncryptor stringEncryptor) {
this.stringEncryptor = stringEncryptor;
}
@Pointcut("@annotation(com.beiming.cloud.user.annotation.JasyptMethod)")
public void pointCut() {
}
@Around("pointCut()")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
JasyptMethod jasyptMethod = methodSignature.getMethod().getAnnotation(JasyptMethod.class);
String type = jasyptMethod.type();
Object[] objects = joinPoint.getArgs();
if (JasyptConstant.ENCRYPT.equalsIgnoreCase(type)) {
try {
if (objects.length != 0) {
for (int i = 0; i < objects.length; i++) {
if (objects[i] instanceof String) {
objects[i] = stringEncryptor.encrypt(String.valueOf(objects[i]));
} else {
handlerObj(objects[i], JasyptConstant.ENCRYPT, objects[i] instanceof List<?>);
}
}
}
} catch (Exception e) {
log.error("TOOLBOX 数据加密失败", e);
}
}
Object obj = joinPoint.proceed(objects);
if (JasyptConstant.DECRYPT.equalsIgnoreCase(type) && obj != null) {
if (obj instanceof String) {
obj = stringEncryptor.decrypt(String.valueOf(obj));
} else {
handlerObj(obj, JasyptConstant.DECRYPT, obj instanceof List);
}
}
return obj;
}
private void handlerObj(Object obj, String type, boolean isList) {
if (isList) {
List<Object> castList = castList(obj, Object.class);
for (Object o : castList) {
Field[] fieldList = o.getClass().getDeclaredFields();
objRun(o, type, fieldList);
}
} else {
Field[] fields = obj.getClass().getDeclaredFields();
objRun(obj, type, fields);
}
}
private void objRun(Object obj, String type, Field[] fields) {
for (Field field : fields) {
boolean hasSecureField = field.isAnnotationPresent(JasyptField.class);
if (hasSecureField) {
try {
//关闭JDK安全检查,提高反射速度
field.setAccessible(true);
String realValue = (String) field.get(obj);
String value;
if (JasyptConstant.DECRYPT.equals(type)) {
value = stringEncryptor.decrypt(realValue);
} else {
value = stringEncryptor.encrypt(realValue);
}
field.set(obj, value);
} catch (Exception e) {
log.error("TOOLBOX 反射修改对象属性失败", e);
}
}
}
}
public static <T> List<T> castList(Object obj, Class<T> clazz) {
List<T> result = new ArrayList<T>();
if (obj instanceof List<?>) {
for (Object o : (List<?>) obj) {
result.add(clazz.cast(o));
}
return result;
}
return null;
}
}
直接在方法上, 对应实体类加解密字段上配置注解