实现:
- 自定义注解类
- 自定义myabtis拦截器,拦截mybatis,主要涉及三个handler(StatementHandler,ParameterHandler,ResultSetHandler)
- 自定义加解密工具类
- 自定义业务处理Service(根据业务自行开发)
- 自定义注解添加再实体类及需要加解密字段上进行简单增改查测试
- import java.lang.annotation.*;
- /**
- * =====================================
- * *******开发部
- * =====================================
- *
- * @author 开发者
- * @version 1.0-SNAPSHOT
- *
- * @date 2023/2/6
- */
- @Target({ElementType.TYPE, ElementType.PARAMETER})
- @Retention(RetentionPolicy.RUNTIME)
- @Inherited
- @Documented
- public @interface SensitiveEntity {
- }<br><br><br><br>
复制代码- import java.lang.annotation.*;<br><br>/**<br> * =====================================<br> * ***********开发部<br> * =====================================<br> *<br> * @author 开发者<br> * @version 1.0-SNAPSHOT<br> * <br> * @date 2023/2/6<br> */<br>@Target({ElementType.FIELD})<br>@Retention(RetentionPolicy.RUNTIME)<br>@Inherited<br>@Documented<br>public @interface SensitiveField {<br>}
复制代码- 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})
- })
- @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;
- /**
- * @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();
- }
- }
复制代码- 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
- */
- @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 **************.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<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;<br>}<br><br>
复制代码- @Test<br>public void add(){<br> Users user = new Users();<br> user.setUserId("test03");<br> user.setUserName("小明004");<br> user.setNickName("test03");<br> user.setPassword("12343454123");<br> user.setPwdDuration("124124124214");<br><br> usersService.insert(user);<br>}<br><br>@Test<br>public void update(){<br> Users user = new Users();<br> user.setUserId("test03");<br> user.setUserName("小明03");<br> user.setNickName("test03");<br> user.setPassword("1252525125123");<br> user.setPwdDuration("1252525125121");<br> usersService.updateByPrimaryKeySelective(user);<br>}<br><br>@Test<br>public void queryTest(){<br> System.out.println(JSON.toJSON(usersService.selectByPrimaryKey(3)));<br>}
复制代码 本文原创:如有使用请标明出处
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |