最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

mybatis用攔截器實(shí)現(xiàn)字段加解密全過程

 更新時(shí)間:2025年08月08日 11:15:17   作者:Pluto372  
本文通過自定義注解和MyBatis攔截器實(shí)現(xiàn)敏感信息加密,處理Parameter和ResultSet,確保數(shù)據(jù)庫(kù)存儲(chǔ)安全且查詢結(jié)果解密可用

前言

根據(jù)公司業(yè)務(wù)需要,靈活對(duì)客戶敏感信息進(jìn)行加解密,這里采用mybatis攔截器進(jìn)行簡(jiǎn)單實(shí)現(xiàn)個(gè)demo。

攔截器的使用

// 執(zhí)行
Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
// 請(qǐng)求參數(shù)處理
ParameterHandler (getParameterObject, setParameters)
// 返回結(jié)果集處理
ResultSetHandler (handleResultSets, handleOutputParameters)
// SQL語句構(gòu)建
StatementHandler (prepare, parameterize, batch, update, query)

我們要實(shí)現(xiàn)數(shù)據(jù)加密,進(jìn)入數(shù)據(jù)庫(kù)的字段不能是真實(shí)的數(shù)據(jù),但是返回來的數(shù)據(jù)要真實(shí)可用,所以我們需要針對(duì) Parameter 和 ResultSet 兩種類型處理,同時(shí)為了更靈活的使用,我們需要自定義注解。

/**
 *需要加解密的字段注解
**/
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Encryption {
    String encryptionType() default "";
}

編寫一下加解密算法(隨便找的)

**
 * @desc: AES對(duì)稱加密,對(duì)明文進(jìn)行加密、解密處理
 * @author:
 * @createTime: 20231014 上午9:54:52
 * @version: v0.0.1
 */
public class AESUtil {
    private static final String KEY_ALGORITHM = "AES";
    private static final String CIPHER_ALGORITHM = "AES/ECB/PKCS5Padding";


    /**
     * @desc: AES對(duì)稱-加密操作
     * @version: v0.0.1
     * @param keyStr 進(jìn)行了Base64編碼的秘鑰
     * @param data 需要進(jìn)行加密的原文
     * @return String 數(shù)據(jù)密文,加密后的數(shù)據(jù),進(jìn)行了Base64的編碼
     */
    public static String encrypt(String keyStr, String data) throws Exception {
        // 轉(zhuǎn)換密鑰
        Key key = new SecretKeySpec(Base64.getDecoder().decode(keyStr), KEY_ALGORITHM);
        Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
        // 加密
        cipher.init(Cipher.ENCRYPT_MODE, key);
        byte[] result = cipher.doFinal(data.getBytes());
        return Base64.getEncoder().encodeToString(result);
    }
    /**
     * @desc: AES對(duì)稱-解密操作
     * @version: v0.0.1
     * @param keyStr 進(jìn)行了Base64編碼的秘鑰
     * @param data 需要解密的數(shù)據(jù)<span style="color:red;">(數(shù)據(jù)必須是通過AES進(jìn)行加密后,對(duì)加密數(shù)據(jù)Base64編碼的數(shù)據(jù))</span>
     * @return String 返回解密后的原文
     */
    public static String decrypt(String keyStr, String data) throws Exception {
        // 轉(zhuǎn)換密鑰
        Key key = new SecretKeySpec(Base64.getDecoder().decode(keyStr), KEY_ALGORITHM);
        Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
        // 解密
        cipher.init(Cipher.DECRYPT_MODE, key);
        byte[] result = cipher.doFinal(Base64.getDecoder().decode(data));
        return new String(result);
    }


    /**
     * @desc: 生成AES的秘鑰,秘鑰進(jìn)行了Base64編碼的字符串
     * @version: v0.0.1
     * @return String 對(duì)生成的秘鑰進(jìn)行了Base64編碼的字符串
     */
    public static String keyGenerate() throws Exception {
        // 生成密鑰
        KeyGenerator keyGenerator = KeyGenerator.getInstance(KEY_ALGORITHM);
        keyGenerator.init(new SecureRandom());
        SecretKey secretKey = keyGenerator.generateKey();
        byte[] keyBytes = secretKey.getEncoded();
        return Base64.getEncoder().encodeToString(keyBytes);
    }
    public static void main(String[] args) throws Exception {
        System.out.println(
                keyGenerate()
        );

    }
}

編寫一下加解密接口

/**
 * 加解密處理接口
 */
public interface CipherHandler {

    /**
     * 加密
     * @param data 需要加密的數(shù)據(jù)
     * @return 加密結(jié)果
     */
   String encrypt(String data) throws Exception;

    /**
     * 解密
     * @param data 需要加密的數(shù)據(jù)
     * @return 解密結(jié)果
     */
    String decrypt(String data) throws Exception;

}

public class AEScipher implements CipherHandler{
    private final static String keyStr="glRwFSBcKzppYVQiwT8M/Q==";

    @Override
    public String encrypt(String data) throws Exception {
        return AESUtil.encrypt(this.keyStr,data);
    }

    @Override
    public String decrypt(String data) throws Exception {
        return AESUtil.decrypt(this.keyStr,data);
    }

    public static CipherHandler getAEScipher(){
        return new AEScipher();
    }
}

接下來寫一下加解密的攔截器

@Intercepts({
        @Signature(type = ParameterHandler.class, method = "setParameters", args = PreparedStatement.class),
})
@Component
@Slf4j
public class ParameterInterceptor implements Interceptor {

    private CipherHandler cipherHandler = AEScipher.getAEScipher();
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        DefaultParameterHandler parameterHandler = (DefaultParameterHandler) invocation.getTarget();
        // 獲取參數(shù)對(duì)像,即 mapper 中 paramsType 的實(shí)例
        Field parameterField = parameterHandler.getClass().getDeclaredField("parameterObject");
        parameterField.setAccessible(true);
        // 取出實(shí)例
        Object parameterObject = parameterField.get(parameterHandler);
        try {
            // 搜索該方法中是否有需要加密的字段
            List<Field> fieldList = searchParamAnnotation(parameterHandler);
            //加密
            if(!CommonHelper.isEmpty(fieldList)){
                dealParamEncrypt(fieldList,parameterObject);
            }
            parameterField.set(parameterHandler, parameterObject);
            PreparedStatement ps = (PreparedStatement) invocation.getArgs()[0];
            parameterHandler.setParameters(ps);

        } catch (Exception e) {
            log.info(e.getMessage());
        }
        return invocation.proceed();
    }


    /**
     * 查找需要需要加密字段
     * @param parameterHandler
     * @return
     * @throws Exception
     */
    private List<Field> searchParamAnnotation(ParameterHandler parameterHandler) throws Exception {
        Class<DefaultParameterHandler> handlerClass = DefaultParameterHandler.class;
        Field mappedStatementFiled = handlerClass.getDeclaredField("mappedStatement");
        mappedStatementFiled.setAccessible(true);
        MappedStatement mappedStatement = (MappedStatement) mappedStatementFiled.get(parameterHandler);
        String methodName = mappedStatement.getId();
        // 獲取Mapper類對(duì)象
        Class<?> mapperClass = Class.forName(methodName.substring(0, methodName.lastIndexOf('.')));
        methodName = methodName.substring(methodName.lastIndexOf('.') + 1);
        Method[] methods = mapperClass.getDeclaredMethods();
        Method method = null;
        for (Method m : methods) {
            if (m.getName().equals(methodName)) {
                method = m;
                break;
            }
        }
        List<Field> fieldList = new ArrayList<>();
        if (method != null) {
            Annotation[][] pa = method.getParameterAnnotations();
            Parameter[] parameters = method.getParameters();
            for (int i = 0; i < pa.length; i++) {
                Parameter parameter = parameters[i];
                String typeName = parameter.getParameterizedType().getTypeName();
                // 去除泛型導(dǎo)致的ClassNotFoundException
                Class<?> parameterClass = Class.forName(typeName.contains("<") ? typeName.substring(0, typeName.indexOf("<")) : typeName);
                Field[] declaredFields = parameterClass.getDeclaredFields();
                for (Field declaredField : declaredFields) {
                    Annotation annotation = declaredField.getAnnotation(Encryption.class);
                    if(!CommonHelper.isEmpty(annotation))fieldList.add(declaredField);
                }
            }
        }
        return fieldList;
    }

    /**
     * 處理加密類
     * @param fields
     * @param parameterObject
     * @throws Exception
     */
    private void dealParamEncrypt(List<Field> fields,Object parameterObject) throws Exception {
        fields.forEach(declaredField->{
            declaredField.setAccessible(true);
            Object o = null;
            try {
                o = declaredField.get(parameterObject);
                declaredField.set(parameterObject,cipherHandler.encrypt(o.toString()));
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        });

    }

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

    @Override
    public void setProperties(Properties properties) {

    }

}

查詢結(jié)果解密

@Intercepts({
        @Signature(type = ResultSetHandler.class, method = "handleResultSets", args = {Statement.class})
})
@Component
public class ResultSetInterceptor implements Interceptor {

    private CipherHandler cipherHandler = AEScipher.getAEScipher();
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        // 取出查詢的結(jié)果
        Object resultObject = invocation.proceed();
        if (Objects.isNull(resultObject)) {
            return null;
        }
        // 基于selectList
        if (resultObject instanceof List<?>) {
            List<?> resultList = (List<?>) resultObject;
            if (!CommonHelper.isEmpty(resultList)) {
                for (Object obj : resultList) {
                    toDecrypt(obj);
                }
            }
        } else {
            toDecrypt(resultObject);
        }
        return resultObject;
    }

    private void toDecrypt(Object object) throws Exception {
        Class<?> objectClass = object.getClass();
        Field[] declaredFields = objectClass.getDeclaredFields();
        for (Field declaredField : declaredFields) {
            Annotation annotation = declaredField.getAnnotation(Encryption.class);
            if(!CommonHelper.isEmpty(annotation)){
                declaredField.setAccessible(true);
                declaredField.set(object,cipherHandler.decrypt(declaredField.get(object).toString()));
            }
        }
    }

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

    @Override
    public void setProperties(Properties properties) {

    }
}

判空工具類

public class CommonHelper {

    public static boolean isEmpty(Object o){
        if(o==null)return true;
        if(o instanceof String){
          return  ((String) o).isEmpty();
        }else if(o instanceof List){
            return CollectionUtils.isEmpty((List) o) || ((List<?>) o).size()==0 ;
        }else if(o instanceof Map){
            return CollectionUtils.isEmpty((Map) o) || ((Map<?,?>) o).size()==0 ;
        }else {
            return Objects.isNull(o);
        }
    }
}

測(cè)試結(jié)果:

  • 插入數(shù)據(jù)

  • 查詢數(shù)據(jù):

總結(jié)

以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • Java多線程編程中的線程死鎖的問題解決

    Java多線程編程中的線程死鎖的問題解決

    線程死鎖是多線程編程中的一個(gè)常見問題,它發(fā)生在多個(gè)線程互相等待對(duì)方釋放資源的情況下,導(dǎo)致程序無法繼續(xù)執(zhí)行,本文就來介紹一下Java多線程編程中的線程死鎖的問題解決,感興趣的可以了解一下
    2023-08-08
  • SpringCloud Hystrix的使用

    SpringCloud Hystrix的使用

    這篇文章主要介紹了SpringCloud Hystrix的使用,幫助大家更好的理解和學(xué)習(xí)使用SpringCloud,感興趣的朋友可以了解下
    2021-04-04
  • Java二分查找之循環(huán)條件與區(qū)間寫法詳解

    Java二分查找之循環(huán)條件與區(qū)間寫法詳解

    二分查找算法是一種高效的搜索算法,適用于有序數(shù)組,它通過將查找范圍不斷減半,逐步縮小目標(biāo)值的搜索空間,最終找到目標(biāo)元素或確認(rèn)目標(biāo)不存在,這篇文章主要介紹了Java二分查找之循環(huán)條件與區(qū)間寫法的相關(guān)資料,需要的朋友可以參考下
    2026-03-03
  • 淺談Java BitSet使用場(chǎng)景和代碼示例

    淺談Java BitSet使用場(chǎng)景和代碼示例

    這篇文章主要介紹了淺談Java BitSet使用場(chǎng)景和代碼示例,具有一定借鑒價(jià)值,需要的朋友可以參考下。
    2017-12-12
  • Spring 切面執(zhí)行鏈的實(shí)現(xiàn)示例

    Spring 切面執(zhí)行鏈的實(shí)現(xiàn)示例

    在實(shí)際應(yīng)用中,一個(gè)方法通常會(huì)被多個(gè)切面攔截,本文主要介紹了Spring 切面執(zhí)行鏈的實(shí)現(xiàn)示例,具有一定的參考價(jià)值,感興趣的可以了解一下
    2025-10-10
  • Java實(shí)現(xiàn)郵箱找回密碼實(shí)例代碼

    Java實(shí)現(xiàn)郵箱找回密碼實(shí)例代碼

    本篇文章主要介紹了Java實(shí)現(xiàn)郵箱找回密碼實(shí)例代碼,可以通過郵箱找回丟失密碼,具有一定的參考價(jià)值,有需要的可以了解一下。
    2016-11-11
  • java模擬cookie登陸操作

    java模擬cookie登陸操作

    這篇文章主要為大家詳細(xì)介紹了java模擬cookie登陸操作,模擬登陸,取得cookie以記錄身份,下次請(qǐng)求時(shí)發(fā)送cookie以表明身份,感興趣的小伙伴們可以參考一下
    2016-07-07
  • java實(shí)現(xiàn)日歷窗口小程序

    java實(shí)現(xiàn)日歷窗口小程序

    這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)日歷窗口小程序,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-06-06
  • springboot+nacos+gateway實(shí)現(xiàn)灰度發(fā)布的實(shí)例詳解

    springboot+nacos+gateway實(shí)現(xiàn)灰度發(fā)布的實(shí)例詳解

    灰度發(fā)布是一種在軟件部署過程中用于平滑過渡的技術(shù),通過引入灰度發(fā)布SDK和配置網(wǎng)關(guān)策略實(shí)現(xiàn),本文就來介紹一下,感興趣的可以了解一下
    2022-03-03
  • 詳解Spring不同數(shù)據(jù)庫(kù)異常如何抽象的

    詳解Spring不同數(shù)據(jù)庫(kù)異常如何抽象的

    根據(jù)spring-jdbc中的定義,所有的數(shù)據(jù)操作異常都會(huì)轉(zhuǎn)換為 DataAccessException,下面這篇文章主要給大家介紹了關(guān)于Spring不同數(shù)據(jù)庫(kù)異常如何抽象的相關(guān)資料,需要的朋友可以參考下
    2021-09-09

最新評(píng)論

宾阳县| 锡林浩特市| 牟定县| 会理县| 佛坪县| 郸城县| 虞城县| 三江| 永兴县| 化州市| 乌兰浩特市| 武夷山市| 泗水县| 平乐县| 和林格尔县| 肃南| 仪征市| 偃师市| 荆州市| 炉霍县| 阿图什市| 阳春市| 应城市| 新干县| 神农架林区| 南开区| 和龙市| 双城市| 千阳县| 南雄市| 公主岭市| 鄂托克前旗| 新绛县| 新津县| 章丘市| 荔波县| 景泰县| 正阳县| 嘉义市| 岱山县| 宜都市|