
由于业务需要数据库一些表的部分字段需要加密处理例如用户个人信息因此采用了基于 AES-256 的企业级加密方式安装依赖在pom文件引入下面依赖!-- 加密模块 --dependencygroupIdcn.hutool/groupIdartifactIdhutool-all/artifactIdversion5.8.25/version/dependency配置秘钥秘钥需为32位、64位、128位、256位#敏感信息加密 方式一security:sensitive:enable:true# flag: 00000000000000000000000000000000aes-key:1234567890abcdef1234567890abcdef# 32位(AES256)#---------------------------------------------------#敏感信息加密 方式二security:sensitive:enable:trueaes-key:${AES_KEY}# 32位(AES256)定义注解说明被注解修饰的实体类的属性会被加密处理packagecom.tone.modules.security;importjava.lang.annotation.ElementType;importjava.lang.annotation.Retention;importjava.lang.annotation.RetentionPolicy;importjava.lang.annotation.Target;Target(ElementType.FIELD)Retention(RetentionPolicy.RUNTIME)publicinterfaceSensitiveField{}编写工具类(utils)1. AESUtils/** AESUtils **/packagecom.tone.modules.security.utils;importcn.hutool.crypto.SecureUtil;importcom.tone.modules.security.SensitiveProperties;importorg.springframework.stereotype.Component;ComponentpublicclassAESUtil{privatestaticSensitivePropertiesproperties;/** * 密文前缀 */privatestaticfinalStringPREFIXENC:;publicAESUtil(SensitivePropertiesproperties){AESUtil.propertiesproperties;}publicstaticStringencrypt(Stringtext){if(textnull){returnnull;}StringcipherSecureUtil.aes(properties.getAesKey().getBytes()).encryptHex(text);returnPREFIXcipher;}publicstaticStringdecrypt(Stringtext){if(textnull){returnnull;}Stringciphertext.substring(PREFIX.length());returnSecureUtil.aes(properties.getAesKey().getBytes()).decryptStr(cipher);}publicstaticbooleanisEncrypted(Stringvalue){if(valuenull){returnfalse;}// 举例密文统一以 ENC: 开头returnvalue.startsWith(PREFIX);}}2. SensitiveUtilpackagecom.tone.modules.security.utils;importcom.jeesite.common.entity.Page;importcom.tone.modules.security.SensitiveField;importjava.lang.reflect.Field;importjava.util.*;importjava.util.concurrent.ConcurrentHashMap;publicclassSensitiveUtil{/** * 字段缓存 */privatestaticfinalConcurrentHashMapClass?,ListFieldCACHEnewConcurrentHashMap();/** * 加密 */publicstaticvoidencrypt(Objectobj){process(obj,true);}/** * 解密 */publicstaticvoiddecrypt(Objectobj){process(obj,false);}/** * List加密 */publicstaticvoidencrypt(Collection?list){if(listnull){return;}list.forEach(SensitiveUtil::encrypt);}/** * List解密 */publicstaticvoiddecrypt(Collection?list){if(listnull){return;}list.forEach(SensitiveUtil::decrypt);}/** * Page解密 */publicstaticvoiddecrypt(Page?page){if(pagenull){return;}decrypt(page.getList());}/** * Page加密 */publicstaticvoidencrypt(Page?page){if(pagenull){return;}encrypt(page.getList());}/** * 核心处理 */privatestaticvoidprocess(Objectobj,booleanencrypt){if(objnull){return;}ListFieldfieldsgetSensitiveFields(obj.getClass());for(Fieldfield:fields){try{Objectvaluefield.get(obj);if(valuenull){continue;}Stringtextvalue.toString();if(text.isEmpty()){continue;}if(encrypt){if(AESUtil.isEncrypted(text)){continue;}field.set(obj,AESUtil.encrypt(text));}else{if(!AESUtil.isEncrypted(text)){continue;}try{field.set(obj,AESUtil.decrypt(text));}catch(Exceptione){// 解密失败保持原值}}}catch(Exceptione){thrownewRuntimeException(e);}}}/** * 获取敏感字段 */privatestaticListFieldgetSensitiveFields(Class?clazz){returnCACHE.computeIfAbsent(clazz,c-{ListFieldlistnewArrayList();Class?currentc;while(current!Object.class){for(Fieldfield:current.getDeclaredFields()){if(field.isAnnotationPresent(SensitiveField.class)){field.setAccessible(true);list.add(field);}}currentcurrent.getSuperclass();}returnlist;});}}定义继承类为了方便不同业务实体类加密编写一个公共层用以统一加密处理参照 jeesite 的CrudService创建SensitiveCrudService业务 Service 再继承SensitiveCrudService这样所有继承SensitiveCrudService都会在特定方法调用时候自动对实体类做加密处理结构如下CrudService↑SensitiveCrudService↑UserServiceEmployeeServiceMemberService…代码如下packagecom.tone.modules.security.service;importcom.jeesite.common.dao.CrudDao;importcom.jeesite.common.entity.DataEntity;importcom.jeesite.common.entity.Page;importcom.jeesite.common.service.CrudService;importcom.tone.modules.security.utils.SensitiveUtil;importjavax.transaction.Transactional;importjava.util.List;publicabstractclassSensitiveCrudServiceDextendsCrudDaoT,TextendsDataEntity?extendsCrudServiceD,T{OverridepublicTget(Tentity){Tresultsuper.get(entity);SensitiveUtil.decrypt(result);returnresult;}// Override// public ListT findList(T entity) {//// ListT list super.findList(entity);//// SensitiveUtil.decrypt(list);//// return list;// }//// Override// public PageT findPage(T entity) {//// PageT page super.findPage(entity);//// SensitiveUtil.decrypt(page);//// return page;// }OverrideTransactionalpublicvoidsave(Tentity){SensitiveUtil.encrypt(entity);super.save(entity);}}业务实体类对phone、address、idCard添加注解实现三个字段加密publicclassUserInfoextendsDataEntityUserInfo{privateStringuserName;SensitiveFieldprivateStringphone;SensitiveFieldprivateStringaddress;SensitiveFieldprivateStringidCard;}业务 Service业务Service继承SensitiveCrudService在调用SensitiveCrudService中指定加密的方法时候会自动对实体类加密处理进而实现业务实体类特定字段加密TransactionalServicepublicclassUserServiceextendsSensitiveCrudServiceUserDao,User{OverridepublicFapfUserOtherInfoget(UserInfoentity){UserInfouserInfosuper.get(entity);// 业务处理process(userInfo)returnuserInfo;}OverrideTransactionalpublicvoidsave(UserInfoentity){// ------------业务处理------------// -------super.save(entity);// ------------业务处理------------// -------}秘钥管理秘钥比较重要要妥善保存注意到章节配置秘钥有两种方式在开发环境可以将秘钥写入到yml配置文件中如方式一生产环境中建议秘钥单独保存方法是定义AES_KEY变量将变量存储在服务器中方案1yml 环境变量最常用security:sensitive:aes-key:${AES_KEY}1. Linuxlinux 环境下可采用下面方式在服务器放置秘钥exportAES_KEY12345......1. Windows在环境变量的用户变量中创建AES_KEY或者打开cms窗口在窗口中输入以下命令创建setx AES_KEY12345...... 优点yml 不存密钥代码不改运维可控Jenkins 可注入注意若采用 nssm 发布Springboot 应用有可能需要打开nssm, 编辑自己创建的服务在 Environment variables 选项中添加 AES_KEY 内容如下编辑完成后需要重启 你用nssm注册的serviceAES_KEY12345......方案2Docker / K8S 注入更企业env:-name:AES_KEYvalueFrom:secretKeyRef:name:aes-secretkey:aes-key方案3Spring Boot 外部配置增强java-jarapp.jar --security.sensitive.aes-keyxxxx方案4KMS最佳例如阿里云 KMSAWS KMSVault流程应用 → KMS请求 → 返回解密后的key或直接加密服务