java实现MySQL数据加密存储---自定义注解+自定义mybatis拦截器
思路 调用拦截器 实现加解密
通过自定义加解密拦截器 判断是否是 加解密实体类 的字段 调用 加解密处理类 执行具体算法加解密
自定义类注解
/** * 注解敏感信息类的注解 */ @Inherited @Target({ ElementType.TYPE }) @Retention(RetentionPolicy.RUNTIME) public @interface SensitiveData { }
自定义字段注解
/** * 注解敏感信息类中敏感字段的注解 */ @Inherited @Target({ ElementType.FIELD }) @Retention(RetentionPolicy.RUNTIME) public @interface SensitiveField { }
实体类加上 自定义类注解 字段加上 自定义字段注解
@SensitiveData @TableName(value ="vip_card") @Data public class VipCardVO implements Serializable { /** * 主键 */ @TableId(type = IdType.AUTO) private Integer id; /** * 卡号 */ @SensitiveField private String cardNo; /** * 用户名 */ @SensitiveField private String name; /** * 性别 */ private Integer gender; /** * 年龄 */ private Integer age; /** * 邮箱 */ private String email; /** * 身份证号 */ private String idNumber; /** * 地址 */ private String address; /** * 手机号 */ private String phoneNumber; /** * 创建时间 */ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone="Asia/Shanghai") private Date createTime; /** * 更新时间 */ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone="Asia/Shanghai") private Date updateTime; }
加解密处理类
@Component public class AEScrypto { private final static String password = "V********="; /** * 加密 * * @param declaredFields paramsObject所声明的字段 * @param paramsObject mapper中paramsType的实例 * @return T * @throws IllegalAccessException 字段不可访问异常 */ public static <T> T encrypt(Field[] declaredFields, T paramsObject) throws IllegalAccessException { for (Field field : declaredFields) { //取出所有被EncryptDecryptField注解的字段 SensitiveField sensitiveField = field.getAnnotation(SensitiveField.class); if (!Objects.isNull(sensitiveField)) { field.setAccessible(true); Object object = field.get(paramsObject); //暂时只实现String类型的加密 if (object instanceof String) { String value = (String) object; //加密 这里我使用自定义的AES加密工具 field.set(paramsObject, AESUtil.encrypt(value,password)); } } } return paramsObject; } /** * 解密 * * @param result resultType的实例 * @return T * @throws IllegalAccessException 字段不可访问异常 */ public static <T> T decrypt(T result) throws IllegalAccessException { //取出resultType的类 Class<?> resultClass = result.getClass(); Field[] declaredFields = resultClass.getDeclaredFields(); for (Field field : declaredFields) { //取出所有被EncryptDecryptField注解的字段 SensitiveField sensitiveField = field.getAnnotation(SensitiveField.class); if (!Objects.isNull(sensitiveField)) { field.setAccessible(true); Object object = field.get(result); //只支持String的解密 if (object instanceof String) { String value = (String) object; //对注解的字段进行逐一解密 field.set(result, AESUtil.decrypt(value,password)); } } } return result; } }
加解密工具算法工具类
public class AESUtil { private static final String KEY_ALGORITHM = "AES"; private static final String DEFAULT_CIPHER_ALGORITHM = "AES/ECB/PKCS5Padding";//默认的加密算 /** * AES 加密操作 * * @param content * @param password * @return */ public static String encrypt(String content, String password) { try { Cipher cipher = Cipher.getInstance(DEFAULT_CIPHER_ALGORITHM); byte[] byteContent = content.getBytes("utf-8"); cipher.init(Cipher.ENCRYPT_MODE, getSecretKey(password));// 初始化为加密模式的密码器 byte[] result = cipher.doFinal(byteContent);// 加密 String encode = Base64.getEncoder().encodeToString(result); //encode. return encode;//通过Base64转码返回 } catch (Exception ex) { Logger.getLogger(AESUtil.class.getName()).log(Level.SEVERE, null, ex); } return null; } /** * AES 解密操作 * * @param content * @param password * @return */ public static String decrypt(String content, String password) { try { Cipher cipher = Cipher.getInstance(DEFAULT_CIPHER_ALGORITHM); cipher.init(Cipher.DECRYPT_MODE, getSecretKey(password)); //执行操作 byte[] result = cipher.doFinal(Base64.getDecoder().decode(content)); return new String(result, "utf-8"); } catch (Exception ex) { // log.error(ex.getMessage(),ex); } return null; } }
加密拦截器 实现mybatis 拦截器 接口
@Component @Intercepts({ @Signature(type = ParameterHandler.class, method = "setParameters", args = PreparedStatement.class), }) public class EncryptInterceptor implements Interceptor { @Override public Object intercept(Invocation invocation) throws Throwable { //@Signature 指定了 type= parameterHandler 后,这里的 invocation.getTarget() 便是parameterHandler //若指定ResultSetHandler ,这里则能强转为ResultSetHandler ParameterHandler parameterHandler = (ParameterHandler) invocation.getTarget(); // 获取参数对像,即 mapper 中 paramsType 的实例 Field parameterField = parameterHandler.getClass().getDeclaredField("parameterObject"); parameterField.setAccessible(true); //取出实例 Object parameterObject = parameterField.get(parameterHandler); if (parameterObject != null) { Class<?> parameterObjectClass = parameterObject.getClass(); //校验该实例的类是否被@SensitiveData所注解 SensitiveData sensitiveData = AnnotationUtils.findAnnotation(parameterObjectClass, SensitiveData.class); if (Objects.nonNull(sensitiveData)) { //取出当前当前类所有字段,传入加密方法 Field[] declaredFields = parameterObjectClass.getDeclaredFields(); AEScrypto.encrypt(declaredFields, parameterObject); } } return invocation.proceed(); } /** * 切记配置,否则当前拦截器不会加入拦截器链 */ @Override public Object plugin(Object o) { return Plugin.wrap(o, this); } /** * 自定义配置写入,没有自定义配置的可以直接置空此方法 */ @Override public void setProperties(Properties properties) { } }
解密拦截器 实现mybatis 拦截器 接口
@Component @Intercepts({ @Signature(type = ResultSetHandler.class, method = "handleResultSets", args = {Statement.class}) }) public class DecryptInterceptor implements Interceptor { @Override public Object intercept(Invocation invocation) throws Throwable { //取出查询的结果 Object resultObject = invocation.proceed(); if (Objects.isNull(resultObject)) { return null; } //基于selectList if (resultObject instanceof ArrayList) { ArrayList resultList = (ArrayList) resultObject; if (!CollectionUtils.isEmpty(resultList) && needToDecrypt(resultList.get(0))) { for (Object result : resultList) { //逐一解密 AEScrypto.decrypt(result); } } //基于selectOne } else { if (needToDecrypt(resultObject)) { AEScrypto.decrypt(resultObject); } } return resultObject; } private boolean needToDecrypt(Object object) { Class<?> objectClass = object.getClass(); SensitiveData sensitiveData = AnnotationUtils.findAnnotation(objectClass, SensitiveData.class); return Objects.nonNull(sensitiveData); } @Override public Object plugin(Object target) { return Plugin.wrap(target, this); } @Override public void setProperties(Properties properties) { } }
古人学问无遗力,少壮工夫老始成。
纸上得来终觉浅,绝知此事要躬行。