实现:

  1.   自定义注解类
  2.   自定义myabtis拦截器,拦截mybatis,主要涉及三个handler(StatementHandler,ParameterHandler,ResultSetHandler)
  3.   自定义加解密工具类
  4. 自定义业务处理Service(根据业务自行开发)
  5.   自定义注解添加再实体类及需要加解密字段上进行简单增改查测试
  • 1. 自定义注解类
import java.lang.annotation.*;/** * ===================================== * *******开发部 * ===================================== * * @author 开发者 * @version 1.0-SNAPSHOT * * @date 2023/2/6 */@Target({ElementType.TYPE, ElementType.PARAMETER})@Retention(RetentionPolicy.RUNTIME)@Inherited@Documentedpublic @interface SensitiveEntity {}



import java.lang.annotation.*;

/**
* =====================================
* ***********开发部
* =====================================
*
* @author 开发者
* @version 1.0-SNAPSHOT
*
* @date 2023/2/6
*/
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface SensitiveField {
}
  • 2. 自定义拦截器
import ********.SensitiveEntity;import ********.AesService;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})})@Slf4jpublic 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);    }}
  • 3. 自定义解密工具类
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@Slf4jpublic 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;    /**     * @param content 加密字符串     * @return 加密结果     */    public String encrypt(String content) {        return encrypt(content, encryptKey);    }    /**     * 加密     *     * @param content 加密参数     * @param key     加密key     * @return 结果字符串     */    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();    }}
  • 4. 自定义业务处理Service
import *****************.SensitiveField;import *****************.AesService;import *****************.AesFieldUtils;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 */@Servicepublic class AesServiceImpl implements AesService {    @Value("${aes.key}")    private String key;    @Value("${aes.keyField}")    private String keyField;    @Resource    private AesFieldUtils aesFieldUtils;    @Override    public  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 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;    }}
  • 5. 测试代码
import **************.SensitiveEntity;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 {    /**     *     * 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)));
}

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