Mybatis的 BaseTypeHandler

场景:在生产数据库中有很多的敏感信息是不能直接存储,需要先进行加密然后再存储,比如姓名、身份证、银行卡号等

        <!-- https://mvnrepository.com/artifact/org.apache.shiro/shiro-core -->
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-core</artifactId>
            <version>1.2.4</version>
        </dependency>

1.首先要集成mybatis扩展的BaseTypeHandler,重写钩子方法,在方法中写入我们的加密逻辑,这样在插入以及取出数据的时候就可以做这个加解密,而外部无需感知

package com.wsf.spring.mybatis.handler2;

import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;

import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

/**
 * @author wsf
 * @since 20220120
 */
@Slf4j
public class ConfidentialHandler extends BaseTypeHandler<String> {

    @Override
    public void setNonNullParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType) throws SQLException {
        if(parameter != null){
            try {
                String encryptAES = EncryptionHelper.encryptAES(parameter);
                ps.setString(i,encryptAES);
            } catch (Exception e) {
                log.error("AES加密错误",e);
            }
        }

    }

    @Override
    public String getNullableResult(ResultSet rs, String columnName) throws SQLException {
        try {
            if (rs.getString(columnName) != null) {
                return EncryptionHelper.decryptAES(rs.getString(columnName));
            }
        } catch (Exception e) {
            log.error("AES解密错误", e);
        }
        return null;
    }

    @Override
    public String getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
        try {
            if (rs.getString(columnIndex) != null) {
                return EncryptionHelper.decryptAES(rs.getString(columnIndex));
            }
        } catch (Exception e) {
            log.error("AES解密错误", e);
        }
        return null;
    }

    @Override
    public String getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
        try {
            if (cs.getString(columnIndex) != null) {
                return EncryptionHelper.decryptAES(cs.getString(columnIndex));
            }
        } catch (Exception e) {
            log.error("AES解密错误", e);
        }
        return null;
    }
}

加密工具类

package com.wsf.spring.mybatis.handler2;

import org.apache.shiro.codec.Base64;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;

public class EncryptionHelper {
    /*
     * 加密用的Key 可以用26个字母和数字组成 使用AES-128-CBC加密模式,key需要为16位。
     */
    private static final String key="hj7x89H$yuBI0456";
    private static final String iv ="NIfb&95GUY86Gfgh";

    /**
     * 明文字段加密
     *
     * @param data 数据明文
     * @return 加密数据
     * @throws Exception 异常信息
     */
    public static String encryptAES(String data) throws Exception {
        try {
            Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
            int blockSize = cipher.getBlockSize();
            byte[] dataBytes = data.getBytes();
            int plaintextLength = dataBytes.length;

            if (plaintextLength % blockSize != 0) {
                plaintextLength = plaintextLength + (blockSize - (plaintextLength % blockSize));
            }

            byte[] plaintext = new byte[plaintextLength];
            System.arraycopy(dataBytes, 0, plaintext, 0, dataBytes.length);

            SecretKeySpec keyspec = new SecretKeySpec(key.getBytes(), "AES");
            IvParameterSpec ivspec = new IvParameterSpec(iv.getBytes());  // CBC模式,需要一个向量iv,可增加加密算法的强度

            cipher.init(Cipher.ENCRYPT_MODE, keyspec, ivspec);
            byte[] encrypted = cipher.doFinal(plaintext);

            return EncryptionHelper.encode(encrypted).trim(); // BASE64做转码。

        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 数据解密
     *
     * @param data 数据密文
     * @return 数据明文
     * @throws Exception 异常信息
     */
    public static String decryptAES(String data) throws Exception {
        try
        {
            byte[] encrypted1 = EncryptionHelper.decode(data);//先用base64解密

            Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
            SecretKeySpec keyspec = new SecretKeySpec(key.getBytes(), "AES");
            IvParameterSpec ivspec = new IvParameterSpec(iv.getBytes());

            cipher.init(Cipher.DECRYPT_MODE, keyspec, ivspec);

            byte[] original = cipher.doFinal(encrypted1);
            String originalString = new String(original, StandardCharsets.UTF_8);
            return originalString.trim();
        }
        catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    public static String encode(byte[] byteArray) {
        return new String(Base64.encode(byteArray),StandardCharsets.UTF_8);
    }

    public static byte[] decode(String base64EncodedString) {
        return Base64.decode(base64EncodedString);
    }
}

3.在需要使用到的地方指定这个handler,否则不生效

Mybatis的 BaseTypeHandler

 4.通过测试方法就可以看到对敏感字段进行了加密

Mybatis的 BaseTypeHandler

上一篇:opencv 异常报错Microsoft C++ 异常: cv::Exception,位于内存位置 0x00000058F84FEC20 处


下一篇:SpringBoot集成Swagger2出现:Failed to start bean 'documentationPluginsBootstrapper'; nested exc