核心是通过自定义@DecryptField注解标记字段,结合泛型injectDecrypt方法,利用反射与解密工具在运行时自动解密并注入,不侵入业务、支持扩展、精准控制范围。

核心思路是:用自定义注解标记需要解密的字段,结合泛型方法统一处理不同类型的实体对象,在运行时通过反射+解密工具完成自动注入,不侵入业务逻辑、支持扩展、可精准控制解密范围。
一、定义面向字段的解密注解
注解需作用于字段,且保留到运行时,支持指定解密算法和密钥来源(如配置项名):
- @Retention(RetentionPolicy.RUNTIME) 和 @Target(ElementType.FIELD) 是必须的
- 建议增加 value() 字段用于传入配置键,例如 @DecryptField("user.phone.key"),避免硬编码密钥
- 可选添加 fallback() 或 ignoreIfNull() 等语义化参数,提升健壮性
二、编写泛型解密注入器
关键在于一个能接受任意类型 T 的静态方法,内部完成字段扫描、条件判断与安全赋值:
- 方法签名形如:public static <T> void injectDecrypt(T target)
- 遍历 target.getClass().getDeclaredFields(),跳过 static/final 字段
- 对每个带 @DecryptField 的字段,调用 field.setAccessible(true),再获取其原始值(String 类型优先)
- 根据注解 value() 查 Spring Environment 或配置中心获取密钥,调用 AES/SM4 工具类解密
- 解密成功后,用 field.set(target, decryptedValue) 写回;失败则按策略记录日志或抛定制异常
三、在关键节点触发注入
不能依赖构造器(字段尚未赋值),也不宜手动调用。推荐以下两种生产就绪方式:
- Controller 层入参后置处理:用 @ModelAttribute 或 HandlerMethodArgumentResolver 封装泛型注入逻辑,对标注了 @DecryptField 的 DTO 自动解密
- MyBatis ResultSetHandler 拦截:查询数据库后、映射为对象前,对结果对象执行 injectDecrypt(),确保返回给上层的是明文数据
- 若使用 Lombok @Data,注意 getter/setter 不影响反射赋值;若字段为 final,需提前在构造中绕过(不推荐)
四、安全与可维护性要点
生产环境必须规避常见陷阱:
- 解密失败不能静默吞掉——要统一抛出 DecryptException 并被全局异常处理器捕获,返回明确错误码
- 密钥绝不写死在代码里,必须从 Config Server、Nacos 或 Jasypt 加密配置中动态拉取
- 敏感字段类型不限于 String,可扩展支持 byte[]、LocalDateTime(如加密时间戳),靠泛型方法内 type check 分支处理
- 添加单元测试:构造含 @DecryptField 的测试 POJO,验证空值、密文格式错误、密钥缺失等边界场景

















