一、前言:数据安全防护的必要性

在数字化时代,数据安全已成为企业生存和发展的基石。根据《数据安全法》和《个人信息保护法》的要求,敏感个人信息必须采取相应的加密等安全措施。然而,在实际开发中,数据加密面临着诸多挑战:

传统加密方式的痛点:

  • 代码重复率高,加解密逻辑散落在各个业务方法中

  • 维护困难,新增加密字段需要修改多处代码

  • 容易遗漏,某个查询忘记解密导致数据异常

  • 测试复杂,需要同时验证业务逻辑和加密逻辑

本文提供的解决方案优势:

  • 声明式加密:通过注解标记加密字段

  • 透明化处理:业务代码无需感知加密过程

  • 高性能:基于MyBatis拦截器,性能损耗极小

  • 易维护:集中管理加密逻辑,降低维护成本

二、解决方案架构设计

2.1 整体架构

text

业务层 (Service)
    ↓
数据访问层 (Mapper) 
    ↓
MyBatis拦截器 (Interceptor)
    ↓ 加密/解密
数据库层 (Database)

2.2 核心组件设计

三、核心实现详解

3.1 加密注解定义

/**
 * 字段加密注解
 * 标记在需要加密的实体类字段上
 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Encrypted {
    
    /**
     * 是否支持模糊查询
     * 如果为true,会额外存储字段的哈希值用于模糊匹配
     * 默认关闭,因为会降低安全性
     */
    boolean supportFuzzyQuery() default false;
    
    /**
     * 加密算法类型
     * 默认使用AES-GCM算法
     */
    Algorithm algorithm() default Algorithm.AES_GCM;
    
    /**
     * 字段敏感度级别
     * 用于日志脱敏和权限控制
     */
    SensitivityLevel level() default SensitivityLevel.HIGH;
    
    public enum Algorithm {
        AES_GCM,      // AES-GCM认证加密
        AES_CBC,      // AES-CBC模式
        SM4           // 国密SM4算法
    }
    
    public enum SensitivityLevel {
        LOW,          // 低敏感度,仅基础加密
        MEDIUM,       // 中敏感度,增加访问控制
        HIGH          // 高敏感度,完整安全防护
    }
}

3.2 加密工具类实现

​
/**
 * 字段加密注解
 * 标记在需要加密的实体类字段上
 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Encrypted {
    
    /**
     * 是否支持模糊查询
     * 如果为true,会额外存储字段的哈希值用于模糊匹配
     * 默认关闭,因为会降低安全性
     */
    boolean supportFuzzyQuery() default false;
    
    /**
     * 加密算法类型
     * 默认使用AES-GCM算法
     */
    Algorithm algorithm() default Algorithm.AES_GCM;
    
    /**
     * 字段敏感度级别
     * 用于日志脱敏和权限控制
     */
    SensitivityLevel level() default SensitivityLevel.HIGH;
    
    public enum Algorithm {
        AES_GCM,      // AES-GCM认证加密
        AES_CBC,      // AES-CBC模式
        SM4           // 国密SM4算法
    }
    
    public enum SensitivityLevel {
        LOW,          // 低敏感度,仅基础加密
        MEDIUM,       // 中敏感度,增加访问控制
        HIGH          // 高敏感度,完整安全防护
    }
}

​

3.3 MyBatis拦截器核心实现


/**
 * 加密拦截器
 * 自动处理字段的加密和解密操作
 */
@Slf4j
@Intercepts({
    @Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class}),
    @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}),
    @Signature(type = Executor.class, method = "queryCursor", args = {MappedStatement.class, Object.class, RowBounds.class})
})
public class EncryptionInterceptor implements Interceptor {
    
    private final ObjectFactory objectFactory;
    private final ObjectWrapperFactory objectWrapperFactory;
    private final ReflectorFactory reflectorFactory;
    
    public EncryptionInterceptor(ObjectFactory objectFactory, 
                               ObjectWrapperFactory objectWrapperFactory,
                               ReflectorFactory reflectorFactory) {
        this.objectFactory = objectFactory;
        this.objectWrapperFactory = objectWrapperFactory;
        this.reflectorFactory = reflectorFactory;
    }
    
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        String methodName = invocation.getMethod().getName();
        Object parameter = invocation.getArgs()[1];
        
        // 处理INSERT/UPDATE操作:加密参数
        if ("update".equals(methodName)) {
            processUpdateParameter(parameter);
        }
        
        // 执行原始SQL操作
        Object result = invocation.proceed();
        
        // 处理SELECT操作:解密结果
        if ("query".equals(methodName) || "queryCursor".equals(methodName)) {
            result = processQueryResult(result);
        }
        
        return result;
    }
    
    /**
     * 处理更新操作的参数加密
     */
    private void processUpdateParameter(Object parameter) {
        if (parameter == null) {
            return;
        }
        
        try {
            // 处理不同类型的参数
            if (parameter instanceof Map) {
                // 处理Map参数
                processMapParameter((Map<?, ?>) parameter);
            } else if (isBasicType(parameter.getClass())) {
                // 基本类型不处理
                return;
            } else if (parameter instanceof Collection) {
                // 处理集合参数
                for (Object item : (Collection<?>) parameter) {
                    encryptObjectFields(item);
                }
            } else {
                // 处理实体对象
                encryptObjectFields(parameter);
            }
        } catch (Exception e) {
            log.error("处理更新参数失败", e);
            throw new CryptoException("参数加密处理失败", e);
        }
    }
    
    /**
     * 处理Map类型的参数
     */
    private void processMapParameter(Map<?, ?> parameterMap) {
        for (Map.Entry<?, ?> entry : parameterMap.entrySet()) {
            Object value = entry.getValue();
            if (value != null && !isBasicType(value.getClass())) {
                encryptObjectFields(value);
            }
        }
    }
    
    /**
     * 加密对象的字段
     */
    private void encryptObjectFields(Object obj) {
        if (obj == null || isBasicType(obj.getClass())) {
            return;
        }
        
        Class<?> clazz = obj.getClass();
        List<Field> encryptedFields = getEncryptedFields(clazz);
        
        if (encryptedFields.isEmpty()) {
            return;
        }
        
        for (Field field : encryptedFields) {
            encryptField(obj, field);
        }
    }
    
    /**
     * 获取需要加密的字段列表
     */
    private List<Field> getEncryptedFields(Class<?> clazz) {
        List<Field> encryptedFields = new ArrayList<>();
        Class<?> currentClass = clazz;
        
        // 遍历类继承层次,获取所有加密字段
        while (currentClass != null && currentClass != Object.class) {
            Field[] fields = currentClass.getDeclaredFields();
            for (Field field : fields) {
                if (field.isAnnotationPresent(Encrypted.class)) {
                    encryptedFields.add(field);
                }
            }
            currentClass = currentClass.getSuperclass();
        }
        
        return encryptedFields;
    }
    
    /**
     * 加密单个字段
     */
    private void encryptField(Object obj, Field field) {
        try {
            field.setAccessible(true);
            Object value = field.get(obj);
            
            if (value instanceof String) {
                String stringValue = (String) value;
                if (StringUtils.isNotBlank(stringValue) && !CryptoUtil.isEncrypted(stringValue)) {
                    Encrypted encryptedAnnotation = field.getAnnotation(Encrypted.class);
                    String encryptedValue = CryptoUtil.encrypt(stringValue, encryptedAnnotation.algorithm());
                    field.set(obj, encryptedValue);
                    
                    log.debug("字段加密完成: {}.{}, 原文: {}, 密文: {}", 
                             obj.getClass().getSimpleName(), 
                             field.getName(),
                             SensitiveDataLogger.maskSensitive(stringValue),
                             encryptedValue);
                }
            }
        } catch (IllegalAccessException e) {
            log.error("字段访问失败: {}.{}", obj.getClass().getSimpleName(), field.getName(), e);
            throw new CryptoException("字段加密失败", e);
        }
    }
    
    /**
     * 处理查询结果解密
     */
    private Object processQueryResult(Object result) {
        if (result == null) {
            return null;
        }
        
        try {
            if (result instanceof List) {
                // 处理列表结果
                List<?> resultList = (List<?>) result;
                for (Object item : resultList) {
                    decryptObjectFields(item);
                }
            } else if (result instanceof Map) {
                // 处理Map结果
                processMapResult((Map<?, ?>) result);
            } else {
                // 处理单个对象结果
                decryptObjectFields(result);
            }
        } catch (Exception e) {
            log.error("处理查询结果失败", e);
            throw new CryptoException("结果解密处理失败", e);
        }
        
        return result;
    }
    
    /**
     * 解密对象的字段
     */
    private void decryptObjectFields(Object obj) {
        if (obj == null || isBasicType(obj.getClass())) {
            return;
        }
        
        Class<?> clazz = obj.getClass();
        List<Field> encryptedFields = getEncryptedFields(clazz);
        
        for (Field field : encryptedFields) {
            decryptField(obj, field);
        }
    }
    
   
​

/**
 * 加密拦截器
 * 自动处理字段的加密和解密操作
 */
@Slf4j
@Intercepts({
    @Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class}),
    @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}),
    @Signature(type = Executor.class, method = "queryCursor", args = {MappedStatement.class, Object.class, RowBounds.class})
})
public class EncryptionInterceptor implements Interceptor {
    
    private final ObjectFactory objectFactory;
    private final ObjectWrapperFactory objectWrapperFactory;
    private final ReflectorFactory reflectorFactory;
    
    public EncryptionInterceptor(ObjectFactory objectFactory, 
                               ObjectWrapperFactory objectWrapperFactory,
                               ReflectorFactory reflectorFactory) {
        this.objectFactory = objectFactory;
        this.objectWrapperFactory = objectWrapperFactory;
        this.reflectorFactory = reflectorFactory;
    }
    
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        String methodName = invocation.getMethod().getName();
        Object parameter = invocation.getArgs()[1];
        
        // 处理INSERT/UPDATE操作:加密参数
        if ("update".equals(methodName)) {
            processUpdateParameter(parameter);
        }
        
        // 执行原始SQL操作
        Object result = invocation.proceed();
        
        // 处理SELECT操作:解密结果
        if ("query".equals(methodName) || "queryCursor".equals(methodName)) {
            result = processQueryResult(result);
        }
        
        return result;
    }

​
​
​

/**
 * 加密拦截器
 * 自动处理字段的加密和解密操作
 */
@Slf4j
@Intercepts({
    @Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class}),
    @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}),
    @Signature(type = Executor.class, method = "queryCursor", args = {MappedStatement.class, Object.class, RowBounds.class})
})
public class EncryptionInterceptor implements Interceptor {
    
    private final ObjectFactory objectFactory;
    private final ObjectWrapperFactory objectWrapperFactory;
    private final ReflectorFactory reflectorFactory;
    
    public EncryptionInterceptor(ObjectFactory objectFactory, 
                               ObjectWrapperFactory objectWrapperFactory,
                               ReflectorFactory reflectorFactory) {
        this.objectFactory = objectFactory;
        this.objectWrapperFactory = objectWrapperFactory;
        this.reflectorFactory = reflectorFactory;
    }
    
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        String methodName = invocation.getMethod().getName();
        Object parameter = invocation.getArgs()[1];
        
        // 处理INSERT/UPDATE操作:加密参数
        if ("update".equals(methodName)) {
            processUpdateParameter(parameter);
        }
        
        // 执行原始SQL操作
        Object result = invocation.proceed();
        
        // 处理SELECT操作:解密结果
        if ("query".equals(methodName) || "queryCursor".equals(methodName)) {
            result = processQueryResult(result);
        }
        
        return result;
    }

​

​

3.4 自动配置类

/**
 * 加密自动配置
 */
@Configuration
@ConditionalOnProperty(name = "app.encryption.enabled", havingValue = "true", matchIfMissing = true)
@EnableConfigurationProperties(EncryptionProperties.class)
@AutoConfigureAfter(MybatisAutoConfiguration.class)
public class EncryptionAutoConfiguration {
    
    @Bean
    @ConditionalOnMissingBean
    public EncryptionInterceptor encryptionInterceptor(ObjectFactory objectFactory,
                                                     ObjectWrapperFactory objectWrapperFactory,
                                                     ReflectorFactory reflectorFactory) {
        return new EncryptionInterceptor(objectFactory, objectWrapperFactory, reflectorFactory);
    }
    
    @Bean
    public ConfigurationCustomizer encryptionConfigurationCustomizer(EncryptionInterceptor encryptionInterceptor) {
        return configuration -> {
            configuration.addInterceptor(encryptionInterceptor);
        };
    }
    
    @Bean
    @ConditionalOnMissingBean
    public CryptoUtil cryptoUtil() {
        return new CryptoUtil();
    }
    
    @Bean
    @ConditionalOnMissingBean
    public SensitiveDataLogger sensitiveDataLogger() {
        return new SensitiveDataLogger();
    }
}


/**
 * 加密配置属性
 */
@ConfigurationProperties(prefix = "app.encryption")
@Data
public class EncryptionProperties {
    
    /**
     * 是否启用加密功能
     */
    private boolean enabled = true;
    
    /**
     * 默认加密算法
     */
    private Encrypted.Algorithm defaultAlgorithm = Encrypted.Algorithm.AES_GCM;
    
    /**
     * 密钥配置
     */
    private KeyConfig key = new KeyConfig();
    
    /**
     * 日志配置
     */
    private LogConfig log = new LogConfig();
    
    @Data
    public static class KeyConfig {
        /**
         * AES密钥(Base64编码)
         */
        private String aesKey;
        
        /**
         * SM4密钥(Base64编码)
         */
        private String sm4Key;
        
        /**
         * 密钥获取方式:CONFIG|ENV|KMS
         */
        private String source = "CONFIG";
    }
    
    @Data
    public static class LogConfig {
        /**
         * 是否启用加密日志
         */
        private boolean enabled = true;
        
        /**
         * 日志级别:DEBUG|INFO|WARN|ERROR
         */
        private String level = "DEBUG";
    }
}

3.5 安全日志工具

/**
 * 敏感数据日志处理工具
 * 防止敏感信息在日志中泄露
 */
@Slf4j
public class SensitiveDataLogger {
    
    private static final String PHONE_REGEX = "(?<=\\w{3})\\w(?=\\w{4})";
    private static final String ID_CARD_REGEX = "(?<=\\w{6})\\w(?=\\w{4})";
    private static final String EMAIL_REGEX = "(?<=.{2}).(?=.*@)";
    private static final String BANK_CARD_REGEX = "(?<=\\w{4})\\w(?=\\w{4})";
    
    /**
     * 脱敏手机号
     */
    public static String maskPhone(String phone) {
        if (StringUtils.isBlank(phone)) {
            return phone;
        }
        
        if (phone.length() == 11) {
            return phone.replaceAll(PHONE_REGEX, "*");
        }
        
        // 其他格式的手机号处理
        return maskCommon(phone, 3, 4);
    }
    
    /**
     * 脱敏邮箱
     */
    public static String maskEmail(String email) {
        if (StringUtils.isBlank(email)) {
            return email;
        }
        
        int atIndex = email.indexOf("@");
        if (atIndex <= 0) {
            return "***";
        }
        
        if (atIndex <= 3) {
            return email.substring(0, atIndex).replaceAll(".", "*") + 
                   email.substring(atIndex);
        }
        
        return email.substring(0, 2) + 
               email.substring(2, atIndex).replaceAll(".", "*") + 
               email.substring(atIndex);
    }
​
/**
 * 敏感数据日志处理工具
 * 防止敏感信息在日志中泄露
 */
@Slf4j
public class SensitiveDataLogger {
    
    private static final String PHONE_REGEX = "(?<=\\w{3})\\w(?=\\w{4})";
    private static final String ID_CARD_REGEX = "(?<=\\w{6})\\w(?=\\w{4})";
    private static final String EMAIL_REGEX = "(?<=.{2}).(?=.*@)";
    private static final String BANK_CARD_REGEX = "(?<=\\w{4})\\w(?=\\w{4})";
    
    /**
     * 脱敏手机号
     */
    public static String maskPhone(String phone) {
        if (StringUtils.isBlank(phone)) {
            return phone;
        }
        
        if (phone.length() == 11) {
            return phone.replaceAll(PHONE_REGEX, "*");
        }
        
        // 其他格式的手机号处理
        return maskCommon(phone, 3, 4);
    }
    
    /**
     * 脱敏邮箱
     */
    public static String maskEmail(String email) {
        if (StringUtils.isBlank(email)) {
            return email;
        }
        
        int atIndex = email.indexOf("@");
        if (atIndex <= 0) {
            return "***";
        }
        
        if (atIndex <= 3) {
            return email.substring(0, atIndex).replaceAll(".", "*") + 
                   email.substring(atIndex);
        }
        
        return email.substring(0, 2) + 
               email.substring(2, atIndex).replaceAll(".", "*") + 
               email.substring(atIndex);
    }

​


    
    /**
     * 通用脱敏方法
     */
    public static String maskCommon(String value, int prefixLength, int suffixLength) {
        if (StringUtils.isBlank(value)) {
            return value;
        }
        
        if (value.length() <= prefixLength + suffixLength) {
            // 字符串太短,全部脱敏
            return value.replaceAll(".", "*");
        }
        
        String prefix = value.substring(0, prefixLength);
        String suffix = value.substring(value.length() - suffixLength);
        String middle = value.substring(prefixLength, value.length() - suffixLength)
                           .replaceAll(".", "*");
        
        return prefix + middle + suffix;
    }
    
    /**
     * 智能脱敏敏感数据
     */
    public static String maskSensitive(String value) {
        if (StringUtils.isBlank(value)) {
            return value;
        }
        
        // 根据数据特征判断类型并脱敏
        if (value.matches("^1[3-9]\\d{9}$")) {
            return maskPhone(value);
        } else if (value.contains("@")) {
            return maskEmail(value);
        } else if (value.matches("^[1-9]\\d{5}(18|19|20)\\d{2}((0[1-9])|(1[0-2]))(([0-2][1-9])|10|20|30|31)\\d{3}[0-9Xx]$")) {
            return maskIdCard(value);
        } else if (value.matches("^[1-9]\\d{9,18}$")) {
            return maskBankCard(value);
        } else {
            // 默认脱敏:保留前2后2
            return maskCommon(value, 2, 2);
        }
    }
    
    /**
     * 安全日志记录
     */
    public static void logSafely(String message, Object... args) {
        if (log.isInfoEnabled()) {
            String safeMessage = processMessage(message, args);
            log.info(safeMessage);
        }
    }
    
    /**
     * 处理消息中的敏感参数
     */
    private static String processMessage(String message, Object[] args) {
        if (args == null || args.length == 0) {
            return message;
        }
        
        Object[] safeArgs = Arrays.stream(args)
                .map(arg -> {
                    if (arg instanceof String) {
                        return maskSensitive((String) arg);
                    }
                    return arg;
                })
                .toArray();
        
        return String.format(message, safeArgs);
    }
}

四、使用示例和最佳实践

4.1 实体类定义

/**
 * 用户实体类
 * 演示加密字段的使用
 */
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@TableName("t_user")
public class User {
    
    @TableId(type = IdType.AUTO)
    private Long id;
    
    private String username;
    
    @Encrypted(
        algorithm = Encrypted.Algorithm.AES_GCM,
        level = Encrypted.SensitivityLevel.HIGH
    )
    private String phone;
    
    @Encrypted(
        algorithm = Encrypted.Algorithm.AES_GCM,
        level = Encrypted.SensitivityLevel.MEDIUM
    )
    private String email;
    
    @Encrypted(
        algorithm = Encrypted.Algorithm.AES_GCM,
        level = Encrypted.SensitivityLevel.HIGH,
        supportFuzzyQuery = false  // 身份证号不支持模糊查询
    )
    private String idCard;
    
    @Encrypted(
        algorithm = Encrypted.Algorithm.SM4,
        level = Encrypted.SensitivityLevel.HIGH
    )
    private String bankCard;
    
    private Integer status;
    
    @TableField(fill = FieldFill.INSERT)
    private LocalDateTime createTime;
    
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private LocalDateTime updateTime;
}


/**
 * 员工信息实体
 * 演示不同加密配置
 */
@Data
@TableName("t_employee")
public class Employee {
    
    private Long id;
    private String name;
    private String employeeNo;
    
    @Encrypted(
        algorithm = Encrypted.Algorithm.AES_CBC,
        level = Encrypted.SensitivityLevel.MEDIUM
    )
    private String mobile;
    
    @Encrypted(
        algorithm = Encrypted.Algorithm.AES_GCM,
        level = Encrypted.SensitivityLevel.HIGH
    )
    private String identityNumber;
    
    @Encrypted(
        algorithm = Encrypted.Algorithm.SM4,
        level = Encrypted.SensitivityLevel.HIGH,
        supportFuzzyQuery = true  // 支持按邮箱前缀模糊查询
    )
    private String corporateEmail;
    
    private String department;
}

4.2 业务层使用

/**
 * 用户服务
 * 业务代码完全无需关心加密细节
 */
@Slf4j
@Service
@Transactional
public class UserService {
    
    @Autowired
    private UserMapper userMapper;
    
    @Autowired
    private SensitiveDataLogger sensitiveDataLogger;
    
    /**
     * 创建用户 - 自动加密敏感字段
     */
    public Long createUser(CreateUserRequest request) {
        User user = User.builder()
                .username(request.getUsername())
                .phone(request.getPhone())        // 自动加密
                .email(request.getEmail())        // 自动加密
                .idCard(request.getIdCard())      // 自动加密
                .bankCard(request.getBankCard())  // 自动加密
                .status(1)
                .build();
        
        userMapper.insert(user);
        
        // 安全日志记录
        sensitiveDataLogger.logSafely("创建用户成功, 用户名: {}, 手机号: {}", 
                                    user.getUsername(), user.getPhone());
        
        return user.getId();
    }
​
/**
 * 用户服务
 * 业务代码完全无需关心加密细节
 */
@Slf4j
@Service
@Transactional
public class UserService {
    
    @Autowired
    private UserMapper userMapper;
    
    @Autowired
    private SensitiveDataLogger sensitiveDataLogger;
    
    /**
     * 创建用户 - 自动加密敏感字段
     */
    public Long createUser(CreateUserRequest request) {
        User user = User.builder()
                .username(request.getUsername())
                .phone(request.getPhone())        // 自动加密
                .email(request.getEmail())        // 自动加密
                .idCard(request.getIdCard())      // 自动加密
                .bankCard(request.getBankCard())  // 自动加密
                .status(1)
                .build();
        
        userMapper.insert(user);
        
        // 安全日志记录
        sensitiveDataLogger.logSafely("创建用户成功, 用户名: {}, 手机号: {}", 
                                    user.getUsername(), user.getPhone());
        
        return user.getId();
    }

​
  /**
     * 批量查询用户
     */
    public List<User> batchGetUsers(List<Long> ids) {
        List<User> users = userMapper.selectBatchIds(ids);
        
        // 所有用户的敏感字段都已被自动解密
        users.forEach(user -> {
            sensitiveDataLogger.logSafely("批量查询用户: {}, 手机: {}", 
                                        user.getUsername(), user.getPhone());
        });
        
        return users;
    }
    
    /**
     * 条件查询示例
     */
    public List<User> queryUsers(UserQuery query) {
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        
        if (StringUtils.isNotBlank(query.getUsername())) {
            wrapper.like(User::getUsername, query.getUsername());
        }
        
        // 注意:加密字段不能直接用于条件查询
        // 如果需要查询加密字段,需要特殊处理
        
        return userMapper.selectList(wrapper);
    }
}
/**
 * 用户查询参数
 */
@Data
public class UserQuery {
    private String username;
    private String phone;     // 加密字段,不能直接用于查询条件
    private String email;     // 加密字段,不能直接用于查询条件
    private Integer status;
}

4.3 配置文件

# application.yml
app:
  encryption:
    enabled: true
    default-algorithm: AES_GCM
    key:
      source: CONFIG
      aes-key: "your-aes-base64-key-here"  # 实际项目中从安全配置获取
      sm4-key: "your-sm4-base64-key-here"  # 实际项目中从安全配置获取
    log:
      enabled: true
      level: DEBUG

# MyBatis配置
mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
  mapper-locations: classpath*:mapper/**/*.xml

# 数据源配置
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/test_db?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai
    username: root
    password: your_password
    driver-class-name: com.mysql.cj.jdbc.Driver

# 日志配置
logging:
  level:
    com.example.encryption: DEBUG
  pattern:
    console: "%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{50} - %msg%n"

五、高级特性与优化

5.1 模糊查询支持

/**
 * 模糊查询支持工具
 * 为支持模糊查询的字段创建哈希索引
 */
@Component
public class FuzzyQuerySupport {
    
    /**
     * 为支持模糊查询的字段生成哈希值
     */
    public static String generateFuzzyHash(String value) {
        if (StringUtils.isBlank(value)) {
            return null;
        }
        
        try {
            MessageDigest md = MessageDigest.getInstance("SHA-256");
            byte[] hash = md.digest(value.getBytes(StandardCharsets.UTF_8));
            
            // 取前8字节作为哈希值
            byte[] shortHash = Arrays.copyOf(hash, 8);
            return Base64.getEncoder().encodeToString(shortHash);
        } catch (Exception e) {
            throw new CryptoException("生成模糊查询哈希失败", e);
        }
    }
    
    /**
     * 扩展的加密拦截器,支持模糊查询
     */
    @Intercepts({
        @Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class})
    })
    public static class FuzzyQueryEncryptionInterceptor implements Interceptor {
        
        @Override
        public Object intercept(Invocation invocation) throws Throwable {
            Object parameter = invocation.getArgs()[1];
            
            if (parameter != null && !isBasicType(parameter.getClass())) {
                processFuzzyQueryFields(parameter);
            }
            
            return invocation.proceed();
        }
        
        private void processFuzzyQueryFields(Object obj) {
            Class<?> clazz = obj.getClass();
            Field[] fields = clazz.getDeclaredFields();
            
            for (Field field : fields) {
                Encrypted encrypted = field.getAnnotation(Encrypted.class);
                if (encrypted != null && encrypted.supportFuzzyQuery()) {
                    processFuzzyQueryField(obj, field);
                }
            }
        }
        
        private void processFuzzyQueryField(Object obj, Field field) {
            try {
                field.setAccessible(true);
                Object value = field.get(obj);
                
                if (value instanceof String && StringUtils.isNotBlank((String) value)) {
                    // 生成哈希字段名:原字段名 + "Hash"
                    String hashFieldName = field.getName() + "Hash";
                    Field hashField = obj.getClass().getDeclaredField(hashFieldName);
                    hashField.setAccessible(true);
                    
                    String hashValue = generateFuzzyHash((String) value);
                    hashField.set(obj, hashValue);
                }
            } catch (Exception e) {
                log.error("处理模糊查询字段失败: {}.{}", 
                         obj.getClass().getSimpleName(), field.getName(), e);
            }
        }
    }
}
Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐