本篇文章为你整理了基于拦截器+mybatis+注解 实现对敏感字段进行加解密()的详细内容,包含有 基于拦截器+mybatis+注解 实现对敏感字段进行加解密,希望能帮助你了解 基于拦截器+mybatis+注解 实现对敏感字段进行加解密。
自定义注解类
自定义myabtis拦截器,拦截mybatis,主要涉及三个handler(StatementHandler,ParameterHandler,ResultSetHandler)
自定义加解密工具类
自定义业务处理Service(根据业务自行开发)
自定义注解添加再实体类及需要加解密字段上进行简单增改查测试
}
import java.lang.annotation.*; /** * ===================================== * ***********开发部 * ===================================== * * @author 开发者 * @version 1.0-SNAPSHOT * * @date 2023/2/6 */ @Target({ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME) @Inherited @Documented public @interface SensitiveField { }
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;
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 *****************.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 **************.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;
}
@Test public void add(){ Users user = new Users(); user.setUserId("test03"); user.setUserName("小明004"); user.setNickName("test03"); user.setPassword("12343454123"); user.setPwdDuration("124124124214"); usersService.insert(user); } @Test public void update(){ Users user = new Users(); user.setUserId("test03"); user.setUserName("小明03"); user.setNickName("test03"); user.setPassword("1252525125123"); user.setPwdDuration("1252525125121"); usersService.updateByPrimaryKeySelective(user); } @Test public void queryTest(){ System.out.println(JSON.toJSON(usersService.selectByPrimaryKey(3))); }
本文原创:如有使用请标明出处
以上就是基于拦截器+mybatis+注解 实现对敏感字段进行加解密()的详细内容,想要了解更多 基于拦截器+mybatis+注解 实现对敏感字段进行加解密的内容,请持续关注盛行IT软件开发工作室。
郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。