基于拦截器+mybatis+注解 实现对敏感字段进行加解密()

  本篇文章为你整理了基于拦截器+mybatis+注解 实现对敏感字段进行加解密()的详细内容,包含有 基于拦截器+mybatis+注解 实现对敏感字段进行加解密,希望能帮助你了解 基于拦截器+mybatis+注解 实现对敏感字段进行加解密。

   自定义注解类

   自定义myabtis拦截器,拦截mybatis,主要涉及三个handler(StatementHandler,ParameterHandler,ResultSetHandler)

   自定义加解密工具类

   自定义业务处理Service(根据业务自行开发)

   自定义注解添加再实体类及需要加解密字段上进行简单增改查测试

  
}


 

 

  

import java.lang.annotation.*;
/**
 * =====================================
 * ***********开发部
 * =====================================
 *
 * @author 开发者
 * @version 1.0-SNAPSHOT
 * 
 * @date 2023/2/6
 */
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface SensitiveField {
}

 

  

 

 

  
import lombok.extern.slf4j.Slf4j;

  import org.apache.ibatis.executor.parameter.ParameterHandler;

  import org.apache.ibatis.executor.resultset.ResultSetHandler;

  import org.apache.ibatis.executor.statement.StatementHandler;

  import org.apache.ibatis.mapping.BoundSql;

  import org.apache.ibatis.plugin.Interceptor;

  import org.apache.ibatis.plugin.Intercepts;

  import org.apache.ibatis.plugin.Invocation;

  import org.apache.ibatis.plugin.Signature;

  import org.springframework.core.annotation.AnnotationUtils;

  import org.springframework.stereotype.Component;

  import org.springframework.util.CollectionUtils;

  import javax.annotation.Resource;

  import java.lang.reflect.Field;

  import java.sql.Connection;

  import java.sql.PreparedStatement;

  import java.sql.Statement;

  import java.util.ArrayList;

  import java.util.Objects;

  import java.util.Properties;

   * =====================================

   * ***********开发部

   * =====================================

   * @version 1.0-SNAPSHOT

   * @description

   * @date 2023/2/10

  @Component

  @Intercepts({

   @Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class}),

   @Signature(type = ParameterHandler.class, method = "setParameters", args = PreparedStatement.class),

   @Signature(type = ResultSetHandler.class, method = "handleResultSets", args = {Statement.class})

  @Slf4j

  public class MyBatisInterceptor implements Interceptor {

   @Resource

   private AesService aesService;

   @Override

   public Object intercept(Invocation invocation) throws Throwable {

   Object target = invocation.getTarget();

   //拦截sql结果处理器

   if (target instanceof ResultSetHandler) {

   return resultDecrypt(invocation);

   //拦截sql参数处理器

   if (target instanceof ParameterHandler) {

   return parameterEncrypt(invocation);

   //拦截sql语句处理器

   if (target instanceof StatementHandler) {

   return replaceSql(invocation);

   return invocation.proceed();

   * 对mybatis映射结果进行字段解密

   * @param invocation 参数

   * @return 结果

   * @throws Throwable 异常

   private Object resultDecrypt(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))) {

   return resultObject;

   for (Object result : resultList) {

   //逐一解密

   aesService.decrypt(result);

   //基于selectOne

   } else {

   if (needToDecrypt(resultObject)) {

   aesService.decrypt(resultObject);

   return resultObject;

   * mybatis映射参数进行加密

   * @param invocation 参数

   * @return 结果

   * @throws Throwable 异常

   private Object parameterEncrypt(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 (null == parameterObject) {

   return invocation.proceed();

   Class ? parameterObjectClass = parameterObject.getClass();

   //校验该实例的类是否被@SensitiveEntity所注解

   SensitiveEntity sensitiveEntity = AnnotationUtils.findAnnotation(parameterObjectClass, SensitiveEntity.class);

   //未被@SensitiveEntity所注解 则为null

   if (Objects.isNull(sensitiveEntity)) {

   return invocation.proceed();

   //取出当前当前类所有字段,传入加密方法

   Field[] declaredFields = parameterObjectClass.getDeclaredFields();

   aesService.encrypt(declaredFields, parameterObject);

   return invocation.proceed();

   * 替换mybatis Sql中的加密Key

   * @param invocation 参数

   * @return 结果

   * @throws Throwable 异常

   private Object replaceSql(Invocation invocation) throws Throwable {

   StatementHandler statementHandler = (StatementHandler) invocation.getTarget();

   BoundSql boundSql = statementHandler.getBoundSql();

   //获取到原始sql语句

   String sql = boundSql.getSql();

   sql = aesService.replaceAll(sql);

   if (null == sql){

   return invocation.proceed();

   //通过反射修改sql语句

   Field field = boundSql.getClass().getDeclaredField("sql");

   field.setAccessible(true);

   field.set(boundSql, sql);

   return invocation.proceed();

   * 判断是否包含需要加解密对象

   * @param object 参数

   * @return 结果

   private boolean needToDecrypt(Object object) {

   Class ? objectClass = object.getClass();

   SensitiveEntity sensitiveEntity = AnnotationUtils.findAnnotation(objectClass, SensitiveEntity.class);

   return Objects.nonNull(sensitiveEntity);

   @Override

   public Object plugin(Object target) {

   return Interceptor.super.plugin(target);

   @Override

   public void setProperties(Properties properties) {

   Interceptor.super.setProperties(properties);

  }

 

 

  


import lombok.extern.slf4j.Slf4j;

 

  import org.springframework.beans.factory.annotation.Value;

  import org.springframework.stereotype.Component;

  import org.springframework.util.Base64Utils;

  import javax.crypto.Cipher;

  import javax.crypto.spec.SecretKeySpec;

  import java.util.regex.Matcher;

  import java.util.regex.Pattern;

   * =====================================

   * ****************开发部

   * =====================================

   * @author 开发者

   * @version 1.0-SNAPSHOT

   * @description

   * @date 2023/2/10

  @Component

  @Slf4j

  public class AesFieldUtils {

   * 加密算法

   private final String KEY_ALGORITHM = "AES";

   * 算法/模式/补码方式

   private final String DEFAULT_CIPHER_ALGORITHM = "AES/ECB/PKCS5Padding";

   * 编码格式

   private final String CODE = "utf-8";

   * base64验证规则

   private final String BASE64_RULE = "^([A-Za-z0-9+/]{4})*([A-Za-z0-9+/]{4}[A-Za-z0-9+/]{3}=[A-Za-z0-9+/]{2}==)$";

   * 正则验证对象

   private final Pattern PATTERN = Pattern.compile(BASE64_RULE);

   * 加解密 密钥key

   @Value("${encrypt.key}")

   public static String encryptKey;

  
public String encrypt(String content, String key) {

   //判断如果已经是base64加密字符串则返回原字符串

   if (isBase64(content)) {

   return content;

   byte[] encrypted = encrypt2bytes(content, key);

   if (null == encrypted encrypted.length 1) {

   log.info("加密字符串[{}]转字节为null", content);

   return null;

   return Base64Utils.encodeToString(encrypted);

   * @param content 加密字符串

   * @param key 加密key

   * @return 返回加密字节

   public byte[] encrypt2bytes(String content, String key) {

   try {

   byte[] raw = key.getBytes(CODE);

   SecretKeySpec secretKeySpec = new SecretKeySpec(raw, KEY_ALGORITHM);

   Cipher cipher = Cipher.getInstance(DEFAULT_CIPHER_ALGORITHM);

   cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec);

   return cipher.doFinal(content.getBytes(CODE));

   } catch (Exception e) {

   log.error("failed to encrypt: {} of {}", content, e);

   return null;

   * @param content 加密字符串

   * @return 返回加密结果

   public String decrypt(String content) {

   try {

   return decrypt(content, encryptKey);

   } catch (Exception e) {

   log.error("failed to decrypt: {}, e: {}", content, e);

   return null;

   * 解密

   * @param content 解密字符串

   * @param key 解密key

   * @return 解密结果

   public String decrypt(String content, String key) throws Exception {

   //不是base64格式字符串则不进行解密

   if (!isBase64(content)) {

   return content;

   return decrypt(Base64Utils.decodeFromString(content), key);

   * @param content 解密字节

   * @param key 解密key

   * @return 返回解密内容

   public String decrypt(byte[] content, String key) throws Exception {

   if (key == null) {

   log.error("AES key should not be null");

   return null;

   byte[] raw = key.getBytes(CODE);

   SecretKeySpec keySpec = new SecretKeySpec(raw, KEY_ALGORITHM);

   Cipher cipher = Cipher.getInstance(DEFAULT_CIPHER_ALGORITHM);

   cipher.init(Cipher.DECRYPT_MODE, keySpec);

   try {

   byte[] original = cipher.doFinal(content);

   return new String(original, CODE);

   } catch (Exception e) {

   log.error("failed to decrypt content: {}/ key: {}, e: {}", content, key, e);

   return null;

   * 判断是否为 base64加密

   * @param str 参数

   * @return 结果

   private boolean isBase64(String str) {

   Matcher matcher = PATTERN.matcher(str);

   return matcher.matches();

  }

 

  
import *****************.StringUtils;

  import org.springframework.beans.factory.annotation.Value;

  import org.springframework.stereotype.Service;

  import javax.annotation.Resource;

  import java.lang.reflect.Field;

  import java.util.Objects;

   * =====================================

   * *****************开发部

   * =====================================

   * @author 开发者

   * @version 1.0-SNAPSHOT

   * @description

   * @date 2023/2/10

  @Service

  public class AesServiceImpl implements AesService {

   @Value("${aes.key}")

   private String key;

   @Value("${aes.keyField}")

   private String keyField;

   @Resource

   private AesFieldUtils aesFieldUtils;

   @Override

   public T T encrypt(Field[] declaredFields, T paramsObject) throws Exception {

   for (Field field : declaredFields) {

   //取出所有被EncryptDecryptField注解的字段

   SensitiveField sensitiveField = field.getAnnotation(SensitiveField.class);

   if (Objects.isNull(sensitiveField)) {

   continue;

   field.setAccessible(true);

   Object object = field.get(paramsObject);

   //暂时只实现String类型的加密

   if (object instanceof String) {

   String value = (String) object;

   //如果映射字段值为空,并且以==结尾则跳过不进行加密

   if (!StringUtils.noEmpty(value)) {

   continue;

   //加密 这里我使用自定义的AES加密工具

   field.set(paramsObject, aesFieldUtils.encrypt(value, key));

   return paramsObject;

   @Override

   public T T decrypt(T result) throws Exception {

   //取出resultType的类

   Class ? resultClass = result.getClass();

   Field[] declaredFields = resultClass.getDeclaredFields();

   for (Field field : declaredFields) {

   //取出所有被EncryptDecryptField注解的字段

   SensitiveField sensitiveField = field.getAnnotation(SensitiveField.class);

   if (Objects.isNull(sensitiveField)) {

   continue;

   field.setAccessible(true);

   Object object = field.get(result);

   //只支持String的解密

   if (object instanceof String) {

   String value = (String) object;

   //如果映射字段值为空,并且不已==结尾则跳过不进行解密

   if (!StringUtils.noEmpty(value)) {

   continue;

   //对注解的字段进行逐一解密

   field.set(result, aesFieldUtils.decrypt(value, key));

   return result;

   @Override

   public String replaceAll(String sql) {

   if (sql.contains(keyField)) {

   return sql.replaceAll(keyField, key);

   return null;

  }

 

 

  
import **************.SensitiveField;

  import com.baomidou.mybatisplus.annotation.IdType;

  import com.baomidou.mybatisplus.annotation.TableId;

  import com.baomidou.mybatisplus.annotation.TableName;

  import com.baomidou.mybatisplus.extension.activerecord.Model;

  @SensitiveEntity

  @TableName("t_users")

  public class Users extends Model Users {

   * This field was generated by MyBatis Generator.

   * This field corresponds to the database column t_users.id

   * @mbg.generated Wed Feb 01 10:03:44 CST 2023

   @TableId(value = "id", type = IdType.AUTO)

   private Integer id;

   * This field was generated by MyBatis Generator.

   * This field corresponds to the database column t_users.user_id

   * @mbg.generated Wed Feb 01 10:03:44 CST 2023

   private String userId;

   * This field was generated by MyBatis Generator.

   * This field corresponds to the database column t_users.user_name

   * @mbg.generated Wed Feb 01 10:03:44 CST 2023

   private String userName;

   * This field was generated by MyBatis Generator.

   * This field corresponds to the database column t_users.nick_name

   * @mbg.generated Wed Feb 01 10:03:44 CST 2023

   private String nickName;

   * This field was generated by MyBatis Generator.

   * This field corresponds to the database column t_users.password

   * @mbg.generated Wed Feb 01 10:03:44 CST 2023

   @SensitiveField

   private String password;

   * This field was generated by MyBatis Generator.

   * This field corresponds to the database column t_users.pwd_duration

   * @mbg.generated Wed Feb 01 10:03:44 CST 2023

   private String pwdDuration;

   @SensitiveField

   private String birth;
}


 

 

  

@Test
public void add(){
 Users user = new Users();
 user.setUserId("test03");
 user.setUserName("小明004");
 user.setNickName("test03");
 user.setPassword("12343454123");
 user.setPwdDuration("124124124214");
 usersService.insert(user);
}
@Test
public void update(){
 Users user = new Users();
 user.setUserId("test03");
 user.setUserName("小明03");
 user.setNickName("test03");
 user.setPassword("1252525125123");
 user.setPwdDuration("1252525125121");
 usersService.updateByPrimaryKeySelective(user);
}
@Test
public void queryTest(){
 System.out.println(JSON.toJSON(usersService.selectByPrimaryKey(3)));
}

 

  本文原创:如有使用请标明出处

  以上就是基于拦截器+mybatis+注解 实现对敏感字段进行加解密()的详细内容,想要了解更多 基于拦截器+mybatis+注解 实现对敏感字段进行加解密的内容,请持续关注盛行IT软件开发工作室。

郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。

留言与评论(共有 条评论)
   
验证码: