如何实现非实体Java类对象中指定字段的加密?
Java非实体类指定字段加密最优实现
针对你不需要加密全对象、仅加密部分字段、且类不是持久化Entity的场景,优先选择低侵入、可复用的实现方案,不要硬编码加密逻辑到字段访问方法里。
最推荐方案:自定义标记注解 + 通用反射加密处理器
这个方案几乎不侵入原有业务代码,一次实现所有类都能复用,维护成本最低。
第一步:定义字段标记注解
先写一个运行时生效的字段注解,专门用来标记需要加密的字段:
import java.lang.annotation.*; @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface EncryptField { }
第二步:给需要加密的字段打标记
回到你的employeeDetail类,只需要在要加密的字段上加注解即可,不需要改动原有字段结构、getter/setter逻辑:
public class employeeDetail{ private String employeeID; @EncryptField private String employeeContactNo; // 标记为需要加密 @EncryptField private String employeeAddress; // 标记为需要加密 private String employeeName; // 原有getter、setter、构造方法不需要改动 }
第三步:实现通用加密处理器
写一个通用的处理工具,通过反射扫描对象中带@EncryptField注解的String类型字段,统一执行加解密操作:
import java.lang.reflect.Field; import java.util.Arrays; public class FieldEncryptProcessor { // 替换成你实际使用的加密实现,比如AES、SM4等对称加密算法即可 private final CustomEncryptor encryptor; public FieldEncryptProcessor(CustomEncryptor encryptor) { this.encryptor = encryptor; } /** * 加密对象中所有标记了@EncryptField的字段 */ public <T> T encryptFields(T target) throws IllegalAccessException { if (target == null) return null; Class<?> targetClass = target.getClass(); for (Field field : listAllFields(targetClass)) { // 仅处理带注解、类型为String的字段 if (field.isAnnotationPresent(EncryptField.class) && field.getType().equals(String.class)) { field.setAccessible(true); String originVal = (String) field.get(target); if (originVal != null && !originVal.isBlank()) { // 加密后写回对象 field.set(target, encryptor.encrypt(originVal)); } } } return target; } /** * 解密对象中所有标记了@EncryptField的字段 */ public <T> T decryptFields(T target) throws IllegalAccessException { if (target == null) return null; Class<?> targetClass = target.getClass(); for (Field field : listAllFields(targetClass)) { if (field.isAnnotationPresent(EncryptField.class) && field.getType().equals(String.class)) { field.setAccessible(true); String encryptedVal = (String) field.get(target); if (encryptedVal != null && !encryptedVal.isBlank()) { field.set(target, encryptor.decrypt(encryptedVal)); } } } return target; } // 递归获取类及父类的所有字段,避免父类中标记的字段扫描不到 private Field[] listAllFields(Class<?> clazz) { Field[] declaredFields = clazz.getDeclaredFields(); Class<?> superClazz = clazz.getSuperclass(); if (superClazz != null && superClazz != Object.class) { Field[] superFields = listAllFields(superClazz); Field[] allFields = Arrays.copyOf(declaredFields, declaredFields.length + superFields.length); System.arraycopy(superFields, 0, allFields, declaredFields.length, superFields.length); return allFields; } return declaredFields; } // 自定义加密接口,你自己实现算法逻辑即可 public interface CustomEncryptor { String encrypt(String origin); String decrypt(String encrypted); } }
使用的时候只需要初始化一次处理器,传入你的加密实现,后续要处理任何对象直接调用encryptFields/decryptFields方法就行,不需要针对每个类写单独的加解密逻辑。
这个方案的优势:
- 侵入性极低:哪个字段需要加密就加个注解,不需要改原有业务逻辑
- 复用性强:所有Java类都可以用这个处理器,不需要重复造轮子
- 易维护:后续要更换加密算法、新增/移除需要加密的字段,只需要调整注解标记或者加密实现,不需要散改业务代码
适配特定场景的可选方案:序列化层自动加解密
如果你的对象主要用于JSON序列化传输(比如接口返回、缓存存储),可以直接对接你用的JSON框架(Jackson/Fastjson2等)的扩展点,全局配置一次后,带@EncryptField注解的字段会在序列化输出时自动加密、反序列化读入时自动解密,连手动调用处理器的步骤都能省掉。
注意:这个方案仅适合对象流转依赖JSON序列化的场景,如果只是内存中做字段处理,用上面的通用处理器方案更直接。
不推荐的写法
- 不要在每个字段的getter/setter里硬编码加解密逻辑:后续新增字段、更换算法需要逐个修改类代码,容易漏判,维护成本极高
- 不要把整个对象做整体加密:这种方式必须解密整个对象才能读取非加密字段,灵活性极差
- 不要硬编码字段名做反射判断:字段名重构后会直接失效,没有编译期校验,容易出线上故障
内容的提问来源于stack exchange,提问作者Sachin Rana
相关产品推荐
相关产品推荐

