Mybatis使用拦截器实现数据的加解密

Mybatis使用拦截器实现数据的加解密

实现思路

  1. mybatis配置类中加入自定义拦截器
  2. 自定义拦截器对指定mapper层指定方法(修改或新增相关业务表的方法)进行拦截,对指定属性或字段进行加解密操作

实现代码

// Copyright 2016-2101 Pica.
package com.pica.cloud.patient.health.server.annotation;

import com.google.common.collect.Lists;
import com.pica.cloud.foundation.entity.PicaWarnException;
import com.pica.cloud.patient.health.common.enums.HealthResultCode;
import com.pica.cloud.patient.health.common.utils.EncryptUtils;
import com.pica.cloud.patient.health.server.entity.*;
import com.pica.cloud.patient.health.server.model.patient.PatDocPatModel;
import com.pica.cloud.patient.health.server.util.EmojiFilterUtil;
import org.apache.commons.lang.StringUtils;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.*;

import java.lang.reflect.Field;
import java.util.*;

/**
 * 加密patient patientInfo中的昵称和身份证号
 *
 * @ClassName EncryptNameIdInterceptor
 * @Description
 * @Author Chongwen.jiang
 * @Date 2019/4/3 11:31
 * @ModifyDate 2019/4/3 11:31
 * @Version 1.0
 */
@Intercepts({@Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class})})
public class EncryptNameIdInterceptor implements Interceptor {

    private static final List<String> handlerMethod = Lists.newArrayList(
            "com.pica.cloud.patient.server.mapper.PatPatientInfoMapper.updateByPrimaryKeySelective",
            "com.pica.cloud.patient.server.mapper.PatPatientInfoMapper.insertBatchPatientInfo",
            "com.pica.cloud.patient.server.mapper.PatPatientInfoMapper.updateByExampleSelective",
            "com.pica.cloud.patient.server.mapper.PatPatientMapper.insertBatchPatientBasic",
            "com.pica.cloud.patient.server.mapper.PatHospitalPatInfoMapper.insertBatch",
            "com.pica.cloud.patient.server.mapper.PatHospitalPatInfoMapper.batchUpdate",
            "com.pica.cloud.patient.server.mapper.PatDocPatInfoMapper.insertBatch"
    );

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        final MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0];
        Executor executor = (Executor) invocation.getTarget();
        Object parameter;
        boolean isEncrypted = false;
        parameter = invocation.getArgs()[1];
        BoundSql boundSql = mappedStatement.getBoundSql(parameter);
        //只处理patientInfo 和patient 的mapper里面相关的sql
        if (parameter instanceof PatPatientInfo || parameter instanceof PatPatient || parameter instanceof PatientInfo
                || parameter instanceof PatHospitalPatInfo || parameter instanceof PatDocPatMapping
                || parameter instanceof PatDocPatInfo || parameter instanceof PatDocPatModel
                || handlerMethod.contains(mappedStatement.getId())) {
            Object obj = boundSql.getParameterObject();
            // 加密处理
            isEncrypted = dealPatientValue(obj, true, parameter);
        }

        Object result = executor.update(mappedStatement, parameter);
        if (isEncrypted) {
            //防止多次使用同一个对象,导致字段被多次加密
            Object obj = boundSql.getParameterObject();
            dealPatientValue(obj, false, parameter);
        }
        return result;
    }

    private boolean dealPatientValue(Object obj, boolean encryptFlag, Object parameter) throws IllegalAccessException {
        boolean isEncrypted = false;
        if (obj != null) {
            if (parameter instanceof Map) {
                Map paramMap = (Map) parameter;
                Set<Object> objectSet = new HashSet<>();
                for (Object key : paramMap.keySet()) {
                    Object map = paramMap.get(key);
                    if (map != null && objectSet.add(map)) {
                        if (map.getClass().getName().equals("java.util.ArrayList")) {
                            for (Object param : (ArrayList) map) {
                                isEncrypted = encryptFiled(param.getClass(), param, encryptFlag);
                            }
                        } else {
                            isEncrypted = encryptFiled(map.getClass(), map, encryptFlag);
                        }

                    }
                }
                objectSet.clear();
            } else {
                isEncrypted = encryptFiled(parameter.getClass(), obj, encryptFlag);
            }
        }
        return isEncrypted;
    }

    private boolean encryptFiled(Class<?> clazz, Object object, boolean encryptFlag) throws IllegalAccessException {
        boolean isEncrypted = false;
        //考虑继承的情况
        for (; clazz != Object.class; clazz = clazz.getSuperclass()) {
            Field[] declaredFields = clazz.getDeclaredFields();
            {
                for (Field field : declaredFields) {
                    //加密居民姓名字段
                    if ("nickname".equals(field.getName().toLowerCase())) {
                        field.setAccessible(true);
                        String nickname = (String) field.get(object);
                        if (StringUtils.isNotEmpty(nickname)) {
                            if (encryptFlag) {
                                //过滤emoji表情
                                nickname = EmojiFilterUtil.filterEmoji(nickname);
                                if (StringUtils.isEmpty(nickname)) {
                                    throw new PicaWarnException(HealthResultCode.NICKNAME_EMPTY.code(), HealthResultCode.NICKNAME_EMPTY.message());
                                }
                                field.set(object, EncryptUtils.encryptNickname(nickname));
                                isEncrypted = true;
                            } else {
                                field.set(object, EncryptUtils.decryptNickname(nickname));
                            }
                        }
                    }
                    //加密居民身份证号字段
                    if ("idno".equals(field.getName().toLowerCase())) {
                        field.setAccessible(true);
                        String idNo = (String) field.get(object);
                        if (encryptFlag) {
                            field.set(object, EncryptUtils.encryptIdNo(idNo));
                            isEncrypted = true;
                        } else {
                            field.set(object, EncryptUtils.decryptIdNo(idNo));
                        }
                    }
                }
            }
        }
        return isEncrypted;

    }

    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }

    @Override
    public void setProperties(Properties properties) {

    }
}

// Copyright 2016-2101 Pica.
package com.pica.cloud.patient.health.server.annotation;

import com.pica.cloud.patient.health.common.dto.DocSmsEntityModel;
import com.pica.cloud.patient.health.common.dto.SmsRecordPatient;
import com.pica.cloud.patient.health.common.utils.EncryptUtils;
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.util.ReflectionUtils;

import java.lang.reflect.Field;
import java.util.List;
import java.util.Properties;

/**
 * @ClassName QueryPatientInterceptor
 * @Description 解密查询到的居民数据(居民姓名和身份证号)
 * @Author Chongwen.jiang
 * @Date 2019/4/3 17:59
 * @ModifyDate 2019/4/3 17:59
 * @Version 1.0
 */
@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 QueryPatientInterceptor implements Interceptor {

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        Object[] args = invocation.getArgs();
        MappedStatement ms = (MappedStatement) args[0];
        Object parameter = args[1];
        RowBounds rowBounds = (RowBounds) args[2];
        ResultHandler resultHandler = (ResultHandler) args[3];
        Executor executor = (Executor) invocation.getTarget();
        CacheKey cacheKey;
        BoundSql boundSql;
        if (args.length == 4) {
            //4 个参数时
            boundSql = ms.getBoundSql(parameter);
            cacheKey = executor.createCacheKey(ms, parameter, rowBounds, boundSql);
        } else {
            //6 个参数时
            cacheKey = (CacheKey) args[4];
            boundSql = (BoundSql) args[5];
        }
        List<?> list = executor.query(ms, parameter, rowBounds, resultHandler, cacheKey, boundSql);
        if (list!=null){
            for (Object obj : list) {
                if (obj != null) {
                    decryptPatientValue(obj);
                }
            }
        }

        return  list;
    }

    private void decryptPatientValue(Object obj) throws IllegalAccessException {
        if (obj instanceof DocSmsEntityModel) {
            DocSmsEntityModel info = (DocSmsEntityModel) obj;
            info.setPatientName(EncryptUtils.decryptNickname(info.getPatientName()));
        } else if (obj instanceof SmsRecordPatient) {
            SmsRecordPatient info = (SmsRecordPatient) obj;
            info.setName(EncryptUtils.decryptNickname(info.getName()));
        } else {
            Field[] fields = obj.getClass().getDeclaredFields();
            for (Field field : fields) {
                if ("nickname".equals(field.getName().toLowerCase())) {
                    field.setAccessible(true);
                    String nickname = (String) ReflectionUtils.getField(field, obj);
                    field.set(obj, EncryptUtils.decryptNickname(nickname));
                }
                if ("idno".equals(field.getName().toLowerCase())) {
                    field.setAccessible(true);
                    String idNo = (String) ReflectionUtils.getField(field, obj);
                    field.set(obj, EncryptUtils.decryptIdNo(idNo));
                }
            }
        }
    }

    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }

    @Override
    public void setProperties(Properties properties) {

    }
}


将拦截器注入mybatis配置类中:

// Copyright 2016-2101 Pica.
package com.pica.cloud.patient.health.server.configuration;

import com.pica.cloud.patient.health.server.annotation.EncryptNameIdInterceptor;
import com.pica.cloud.patient.health.server.annotation.QueryPatientInterceptor;
import org.mybatis.spring.boot.autoconfigure.ConfigurationCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * @ClassName MybatisConfig
 * @Description mybatis配置类
 * @Author Chongwen.jiang
 * @Date 2019/4/3 10:21
 * @ModifyDate 2019/4/3 10:21
 * @Version 1.0
 */
@Configuration
public class MybatisConfiguration {

    @Bean
    public ConfigurationCustomizer configurationCustomizer() {
        return configuration -> {
            //修改或新增时加密居民姓名、身份证号
            configuration.addInterceptor(new EncryptNameIdInterceptor());
            //查询时解密居民姓名、身份证号
            configuration.addInterceptor(new QueryPatientInterceptor());
        };
    }
}

// Copyright 2016-2101 Pica.
package com.pica.cloud.patient.health.common.utils;


import com.pica.cloud.foundation.utils.utils.StringUtil;
import org.apache.commons.codec.binary.Hex;
import org.apache.commons.lang3.StringUtils;

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.util.Arrays;

/**
 * @ClassName EncryptUtils
 * @Description 使用AES对称加密 (common-codec)
 * @Author Chongwen.jiang
 * @Date 2019/1/11 10:53
 * @ModifyDate 2019/1/11 10:53
 * @Version 1.0
 */
public class EncryptUtils {

    public static final String COMMON_ID_CARD_KEY = "!abcedf#sadfsd";
    public static final String COMMON_NICKNAME_KEY = "%gGdfadae^DFD";

    public static String encryptAES(String key, String value) {
        try {
            byte[] keyBytes = Arrays.copyOf(key.getBytes("ASCII"), 16);
            SecretKey keyStr = new SecretKeySpec(keyBytes, "AES");
            Cipher cipher = Cipher.getInstance("AES");
            cipher.init(Cipher.ENCRYPT_MODE, keyStr);

            byte[] cleartext = value.getBytes("UTF-8");
            byte[] ciphertextBytes = cipher.doFinal(cleartext);

            return new String(Hex.encodeHex(ciphertextBytes));
        } catch (Exception ex) {
            ex.printStackTrace();
        }

        return null;
    }


    public static String decryptAES(String key, String encrypted) {
        try {
            byte[] keyBytes = Arrays.copyOf(key.getBytes("ASCII"), 16);
            SecretKey keyStr = new SecretKeySpec(keyBytes, "AES");
            Cipher cipher = Cipher.getInstance("AES");
            cipher.init(Cipher.DECRYPT_MODE, keyStr);

            byte[] content = Hex.decodeHex(encrypted.toCharArray());
            byte[] ciphertextBytes = cipher.doFinal(content);

            return new String(ciphertextBytes);
        } catch (Exception ex) {
            ex.printStackTrace();
        }

        return null;
    }


    public static String encryptNickname(String value) {
        return StringUtil.isNull(value) ? null : encryptAES(COMMON_NICKNAME_KEY, value.trim());
    }

    public static String encryptIdNo(String value) {
        return StringUtil.isNull(value) ? null : encryptAES(COMMON_ID_CARD_KEY, value.trim());
    }

    public static String decryptNickname(String value) {
        if (StringUtils.isNotEmpty(value)) {
            String value2 = decryptAES(COMMON_NICKNAME_KEY, value);
            value = StringUtils.isEmpty(value2) ? value : value2;
        }
        return value;
    }

    public static String decryptIdNo(String value) {
        if (StringUtils.isNotEmpty(value)) {
            String value2 = decryptAES(COMMON_ID_CARD_KEY, value);
            value = StringUtils.isEmpty(value2) ? value : value2;
        }
        return value;
    }

    public static void main(String[] args) {
        System.out.println(encryptIdNo("331521199508172351"));
        System.out.println(encryptNickname("哈哈"));
    }

}

mysql加解密函数

AES_ENCRYPT(str,key)

返回用密钥key对字符串str利用高级加密标准算法加密后的结果,调用AES_ENCRYPT的结果是一个二进制字符串,以BLOB类型存储
AES_DECRYPT(str,key) 返回用密钥key对字符串str利用高级加密标准算法解密后的结果
DECODE(str,key) 使用key作为密钥解密加密字符串str
ENCRYPT(str,salt) 使用UNIXcrypt()函数,用关键词salt(一个可以惟一确定口令的字符串,就像钥匙一样)加密字符串str
ENCODE(str,key) 使用key作为密钥加密字符串str,调用ENCODE()的结果是一个二进制字符串,它以BLOB类型存储
MD5() 计算字符串str的MD5校验和
PASSWORD(str) 返回字符串str的加密版本,这个加密过程是不可逆转的,和UNIX密码加密过程使用不同的算法。
SHA() 计算字符串str的安全散列算法(SHA)校验和
示例:
SELECT ENCRYPT(‘root’,‘salt’);
SELECT ENCODE(‘xufeng’,‘key’);
SELECT DECODE(ENCODE(‘xufeng’,‘key’),‘key’);#加解密放在一起
SELECT AES_ENCRYPT(‘root’,‘key’);
SELECT AES_DECRYPT(AES_ENCRYPT(‘root’,‘key’),‘key’);
SELECT MD5(‘123456’);
SELECT SHA(‘123456’);

select AES_DECRYPT(UNHEX(‘AEE1FE30ED93AA2246A249715FFF04BA’),’%gGdfadae^DFD’) from pat_patient
where id=12991809 and delete_flag=1;

SELECT HEX(AES_ENCRYPT(‘老十一’,’%gGdfadae^DFD’)) FROM pat_patient_info WHERE patient_id=12669605 and delete_flag=1;

发布了36 篇原创文章 · 获赞 4 · 访问量 11万+

猜你喜欢

转载自blog.csdn.net/weixin_41205148/article/details/97758882