mybatis字段解密解密

1、加密工具

import org.apache.commons.codec.binary.Base64;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
/**
 * @author: xu.dm
 * @since: 2018/11/24 22:26
 *
 */
public class AesUtils {
    private static final String ALGORITHM = "AES/ECB/PKCS5Padding";
    public static String encrypt(String content, String key) {
        try {
            //获得密码的字节数组
            byte[] raw = key.getBytes();
            //根据密码生成AES密钥
            SecretKeySpec keySpec = new SecretKeySpec(raw, "AES");
            //根据指定算法ALGORITHM自成密码器
            Cipher cipher = Cipher.getInstance(ALGORITHM);
            //初始化密码器,第一个参数为加密(ENCRYPT_MODE)或者解密(DECRYPT_MODE)操作,第二个参数为生成的AES密钥
            cipher.init(Cipher.ENCRYPT_MODE, keySpec);
            //获取加密内容的字节数组(设置为utf-8)不然内容中如果有中文和英文混合中文就会解密为乱码
            byte [] contentBytes = content.getBytes(StandardCharsets.UTF_8);
            //密码器加密数据
            byte [] encodeContent = cipher.doFinal(contentBytes);
            //将加密后的数据转换为字符串返回
            return Base64.encodeBase64String(encodeContent);
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException("AesUtils加密失败");
        }
    }
    public static String decrypt(String encryptStr, String decryptKey) {
        try {
            //获得密码的字节数组
            byte[] raw = decryptKey.getBytes();
            //根据密码生成AES密钥
            SecretKeySpec keySpec = new SecretKeySpec(raw, "AES");
            //根据指定算法ALGORITHM自成密码器
            Cipher cipher = Cipher.getInstance(ALGORITHM);
            //初始化密码器,第一个参数为加密(ENCRYPT_MODE)或者解密(DECRYPT_MODE)操作,第二个参数为生成的AES密钥
            cipher.init(Cipher.DECRYPT_MODE, keySpec);
            //把密文字符串转回密文字节数组
            byte [] encodeContent = Base64.decodeBase64(encryptStr);
            //密码器解密数据
            byte [] byteContent = cipher.doFinal(encodeContent);
            //将解密后的数据转换为字符串返回
            return new String(byteContent, StandardCharsets.UTF_8);
        } catch (Exception e) {
            // e.printStackTrace();
            // 解密失败暂时返回null,可以抛出runtime异常
            return null;
        }
    }
}

2、查询拦截器


import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.cache.CacheKey;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Properties;
/**
 * @author: xu.dm
 * @since: 2022/3/9 11:39
 * 解密数据,返回结果为list集合时,应保证集合里都是同一类型的元素。
 * 解密失败时返回为null,或者返回为空串时,不对原数据操作。
 **/
@Component
@Slf4j
@Intercepts({
        @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}),
        @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class}),
})
public class DecryptionInterceptor implements Interceptor {
    @Value("${AESKey}")
    private String aesKey;
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        Object result = invocation.proceed();
        if (result instanceof ArrayList) {
            @SuppressWarnings("rawtypes")
            ArrayList list = (ArrayList) result;
            if (list.size() == 0) {
                return result;
            }
            if (list.get(0) instanceof Encrypted) {
                for (Object item : list) {
                    decryptField((Encrypted) item);
                }
            }
            return result;
        }
        if (result instanceof Encrypted) {
            decryptField((Encrypted) result);
        }
        return result;
    }
    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }
    @Override
    public void setProperties(Properties properties) {
    }
    /**
     * @param object 待检查的对象
     * @throws IllegalAccessException 通过查询注解@Encrypt或者Encrypted返回的字段,进行解密
     *                                两种方式互斥
     */
    private void decryptField(Encrypted object) throws IllegalAccessException, NoSuchFieldException {
        String[] encryptFields = object.getEncryptFields();
        Class<?> clazz = object.getClass();
        if (encryptFields.length == 0) {
            Field[] fields = clazz.getDeclaredFields();
            for (Field field : fields) {
                field.setAccessible(true);
                Encrypt encrypt = field.getAnnotation(Encrypt.class);
                if (encrypt != null) {
                    if (field.get(object) != null && !StringUtils.isEmpty(field.get(object).toString())) {
                        String encryptString = AesUtils.decrypt(field.get(object).toString(), aesKey);
                        if (!StringUtils.isEmpty(encryptString)) {
                            field.set(object, encryptString);
                            log.debug("Encryption interceptor,encrypt field: {}", field.getName());
                        }
                    }
                }
            }
        } else {
            for (String fieldName : encryptFields) {
                Field field = clazz.getDeclaredField(fieldName);
                field.setAccessible(true);
                if (field.get(object) != null && !StringUtils.isEmpty(field.get(object).toString())) {
                    String encryptString = AesUtils.decrypt(field.get(object).toString(), aesKey);
                    if (!StringUtils.isEmpty(encryptString)) {
                        field.set(object, encryptString);
                        log.debug("Encryption interceptor,encrypt field: {}", field.getName());
                    }
                }
            }
        }
    }
}

3、写操作拦截器


import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.session.defaults.DefaultSqlSession;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
/**
 * @author: xu.dm
 * @since: 2022/3/8
 * 拦截所有实现Encrypted接口的实体类insert和update操作
 * 如果接口的getEncryptFields返回数组长度大于0,则使用该参数进行加密,
 * 否则检查实体类中带@Encrypt注解,对该标识字段加密,
 * 注意:待加密的字段最好是字符串,加密调用的是标识对象的ToString()结果进行加密,
 **/
@Component
@Slf4j
@Intercepts({@Signature(method = "update", type = Executor.class, args = {MappedStatement.class, Object.class})})
public class EncryptionInterceptor implements Interceptor {
    public EncryptionInterceptor() {
    }
    @Value("${AESKey}")
    private String aesKey;
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        Object[] args = invocation.getArgs();
        SqlCommandType sqlCommandType = null;
        for (Object object : args) {
            // 从MappedStatement参数中获取到操作类型
            if (object instanceof MappedStatement) {
                MappedStatement ms = (MappedStatement) object;
                sqlCommandType = ms.getSqlCommandType();
                log.debug("Encryption interceptor 操作类型: {}", sqlCommandType);
                continue;
            }
            log.debug("Encryption interceptor 操作参数:{}", object);
            // 判断参数
            if (object instanceof Encrypted) {
                if (SqlCommandType.INSERT == sqlCommandType) {
                    encryptField((Encrypted) object);
                    continue;
                }
                if (SqlCommandType.UPDATE == sqlCommandType) {
                    encryptField((Encrypted) object);
                    log.debug("Encryption interceptor update operation,encrypt field: {}", object.toString());
                }
            }
            // 兼容批量插入
            if (object instanceof DefaultSqlSession.StrictMap) {
                log.debug("mybatis arg: {}", object);
                DefaultSqlSession.StrictMap map = (DefaultSqlSession.StrictMap) object;
                String key = "collection";
                if (!map.containsKey(key)) {
                    continue;
                }
                ArrayList objs = (ArrayList) map.get(key);
                for (Object obj : objs) {
                    if (obj instanceof Encrypted) {
                        if (SqlCommandType.INSERT == sqlCommandType) {
                            encryptField((Encrypted) obj);
                            continue;
                        }
                        if (SqlCommandType.UPDATE == sqlCommandType) {
                            encryptField((Encrypted) obj);
                            log.debug("Encryption interceptor update operation,encrypt field: {}", obj.toString());
                        }
                    }
                }
            }
        }
        Object resObj = invocation.proceed();
        for (Object object : args) {
            // 从MappedStatement参数中获取到操作类型
            if (object instanceof MappedStatement) {
                MappedStatement ms = (MappedStatement) object;
                sqlCommandType = ms.getSqlCommandType();
                log.debug("Encryption interceptor 操作类型: {}", sqlCommandType);
                continue;
            }
            log.debug("Encryption interceptor 操作参数:{}", object);
            // 判断参数
            if (object instanceof Encrypted) {
                if (SqlCommandType.INSERT == sqlCommandType) {
                    decryptField((Encrypted) object);
                    continue;
                }
                if (SqlCommandType.UPDATE == sqlCommandType) {
                    decryptField((Encrypted) object);
                    log.debug("Encryption interceptor update operation,encrypt field: {}", object.toString());
                }
            }
            // 兼容批量插入
            if (object instanceof DefaultSqlSession.StrictMap) {
                log.debug("mybatis arg: {}", object);
                DefaultSqlSession.StrictMap map = (DefaultSqlSession.StrictMap) object;
                String key = "collection";
                if (!map.containsKey(key)) {
                    continue;
                }
                ArrayList objs = (ArrayList) map.get(key);
                for (Object obj : objs) {
                    if (obj instanceof Encrypted) {
                        if (SqlCommandType.INSERT == sqlCommandType) {
                            decryptField((Encrypted) obj);
                            continue;
                        }
                        if (SqlCommandType.UPDATE == sqlCommandType) {
                            decryptField((Encrypted) obj);
                            log.debug("Encryption interceptor update operation,encrypt field: {}", obj.toString());
                        }
                    }
                }
            }
        }
        return resObj;
    }

    /**
     * @param object 待检查的对象
     * @throws IllegalAccessException 通过查询注解@Encrypt或者Encrypted返回的字段,进行动态加密
     *                                两种方式互斥
     */
    private void encryptField(Encrypted object) throws IllegalAccessException, NoSuchFieldException {
        String[] encryptFields = object.getEncryptFields();
        Class<?> clazz = object.getClass();
        if (encryptFields.length == 0) {
            Field[] fields = clazz.getDeclaredFields();
            for (Field field : fields) {
                field.setAccessible(true);
                Encrypt encrypt = field.getAnnotation(Encrypt.class);
                if (encrypt != null) {
                    if (field.get(object) != null && !StringUtils.isEmpty(field.get(object).toString())) {
                        String encryptString = AesUtils.encrypt(field.get(object).toString(), aesKey);
                        if (!StringUtils.isEmpty(encryptString)) {
                            field.set(object, encryptString);
                            log.debug("Encryption interceptor,encrypt field: {}", field.getName());
                        }
                    }
                }
            }
        } else {
            for (String fieldName : encryptFields) {
                Field field = clazz.getDeclaredField(fieldName);
                field.setAccessible(true);
                if (field.get(object) != null && !StringUtils.isEmpty(field.get(object).toString())) {
                    String encryptString = AesUtils.encrypt(field.get(object).toString(), aesKey);
                    if (!StringUtils.isEmpty(encryptString)) {
                        field.set(object, encryptString);
                        log.debug("Encryption interceptor,encrypt field: {}", field.getName());
                    }
                }
            }
        }
    }
    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }
    @Override
    public void setProperties(Properties properties) {
    }
    /**
     * @param object 待检查的对象
     * @throws IllegalAccessException 通过查询注解@Encrypt或者Encrypted返回的字段,进行解密
     *                                两种方式互斥
     */
    private void decryptField(Encrypted object) throws IllegalAccessException, NoSuchFieldException {
        String[] encryptFields = object.getEncryptFields();
        Class<?> clazz = object.getClass();
        if (encryptFields.length == 0) {
            Field[] fields = clazz.getDeclaredFields();
            for (Field field : fields) {
                field.setAccessible(true);
                Encrypt encrypt = field.getAnnotation(Encrypt.class);
                if (encrypt != null) {
                    if (field.get(object) != null && !StringUtils.isEmpty(field.get(object).toString())) {
                        String encryptString = AesUtils.decrypt(field.get(object).toString(), aesKey);
                        if (!StringUtils.isEmpty(encryptString)) {
                            field.set(object, encryptString);
                            log.debug("Encryption interceptor,encrypt field: {}", field.getName());
                        }
                    }
                }
            }
        } else {
            for (String fieldName : encryptFields) {
                Field field = clazz.getDeclaredField(fieldName);
                field.setAccessible(true);
                if (field.get(object) != null && !StringUtils.isEmpty(field.get(object).toString())) {
                    String encryptString = AesUtils.decrypt(field.get(object).toString(), aesKey);
                    if (!StringUtils.isEmpty(encryptString)) {
                        field.set(object, encryptString);
                        log.debug("Encryption interceptor,encrypt field: {}", field.getName());
                    }
                }
            }
        }
    }
}

4、接口声明


/**
 * @author: xu.dm
 * @since: 2022/3/8 16:30                    
 * 该接口用于标记实体类需要加密,具体的加密内容字段通过getEncryptFields返回.
 * 注意:getEncryptFields与@Encrypt注解可配合使用也可以互斥使用,根据具体的需求实现。
 **/
public interface Encrypted {
    /**
     * 实现该接口,返回需要加密的字段名数组,需与类中字段完全一致,区分大小写
     * @return 返回需要加密的字段
     */
    default String[] getEncryptFields() {
        return new String[0];
    }
}

5、注解声明

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
 * @author : xu.dm
 * @since : 2022/3/8
 * 标识加密的注解,value值暂时没用,根据需要可以考虑采用的加密方式与算法等
 * 注意:Encrypted接口的getEncryptFields与@Encrypt注解可配合使用也可以互斥使用,根据具体的需求实现。
 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Encrypt {
    String value() default "";
}

6、修改数据源

@Autowired(required = false)
private Interceptor[] interceptors;

@Bean
public SqlSessionFactory sqlSessionFactoryStatement() throws Exception {
    SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
    factoryBean.setDataSource(autohomestatement);
    factoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(MAPPER_LLOCATIONS));
    factoryBean.setTypeAliasesPackage(TYPE_ALIASES_PPACKAGE);
    factoryBean.setPlugins(this.interceptors);
    return factoryBean.getObject();
}

7、使用

public class StatementActivityAnswerDetailModifyBO implements Encrypted {
  @Encrypt
private String personName;
}

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值