2026/5/18 20:45:01
网站建设
项目流程
江苏网站seo平台,兰州公司网站建设,什么网站可以做图片,网页的后缀名有那些前言大家好#xff01; 在日常的开发开发工作中#xff0c;我相信各位老铁肯定遇到过这种需求#xff1a; “手机号中间四位得用*显示”、“身份证中间八位要隐藏”、“用户邮箱前缀脱敏”…… 例如#xff1a;手机号#xff1a;13812345678 → 138****5678身份证#xf…前言大家好 在日常的开发开发工作中我相信各位老铁肯定遇到过这种需求“手机号中间四位得用*显示”、“身份证中间八位要隐藏”、“用户邮箱前缀脱敏”……例如手机号13812345678→138****5678身份证430101199003078888→430101********8888姓名张三四→张*四邮箱号12345678qq.com→1234****qq.com银行卡6230351888852405→6230********2405既要展示部分数据又要保证敏感信息不泄露。这就是所谓的数据脱敏。今天给大家分享6种我在项目中常用的脱敏方案SpringBoot项目拿来即用可以直接复制粘贴方案一Hutool工具库懒人必备不想造轮子可以直接用现成的java体验AI代码助手代码解读复制代码// Maven依赖 dependency groupIdcn.hutool/groupId artifactIdhutool-all/artifactId version5.8.0/version /dependency // 使用示例 String phone 13812345678; String maskedPhone DesensitizedUtil.mobilePhone(phone); // 138****5678 String idCard 430101199003078888; String maskedIdCard DesensitizedUtil.idCardNum(idCard, 6, 4); // 430101********8888 String name 张三四; String maskedName DesensitizedUtil.chineseName(name); // 张*四 String email 12345678qq.com; String maskedEmail DesensitizedUtil.email(email); // 1234****qq.com String bankCard 6230351888852405; String maskedBankCard DesensitizedUtil.bankCard(bankCard); // 6230********2405功能全面开箱即用方案二正则工具类写个工具类需要的时候手动处理一下这是最简单直接的方式。java体验AI代码助手代码解读复制代码/** * 脱敏工具类 - 简单直接 */ public class SensitiveUtil { /** * 手机号脱敏13812345678 - 138****5678 */ public static String maskPhone(String phone) { if (StringUtils.isEmpty(phone) || phone.length() ! 11) { return phone; } return phone.replaceAll((\\d{3})\\d{4}(\\d{4}), $1****$2); } /** * 身份证脱敏430101199003078888 - 430101********8888 */ public static String maskIdCard(String idCard) { if (StringUtils.isEmpty(idCard) || idCard.length() 15) { return idCard; } return idCard.replaceAll((\\d{6})\\d{8}(\\w{4}), $1********$2); } /** * 姓名脱敏张三四 - 张*四 */ public static String maskName(String name) { if (StringUtils.isEmpty(name)) { return name; } if (name.length() 1) { return *; } if (name.length() 2) { return name.charAt(0) *; } return name.substring(0, 1) * name.substring(name.length() - 1); } /** * 邮箱脱敏12345678qq.com - 1234****qq.com */ public static String maskEmail(String email) { if (StringUtils.isEmpty(email) || !email.contains()) { return email; } String[] parts email.split(); if (parts[0].length() 4) { return parts[0].substring(0, 1) **** parts[1]; } return parts[0].substring(0, 4) **** parts[1]; } /** * 银行卡脱敏6230351888852405 - 6230********2405 */ public static String maskBankCard(String bankCard) { if (StringUtils.isEmpty(bankCard) || bankCard.length() 8) { return bankCard; } return bankCard.replaceAll((\\d{4})\\d{8}(\\d{4}), $1********$2); } }怎么用很简单java体验AI代码助手代码解读复制代码User user userService.getById(123); // 手动脱敏 user.setPhone(SensitiveUtil.maskPhone(user.getPhone())); user.setIdCard(SensitiveUtil.maskIdCard(user.getIdCard())); user.setName(SensitiveUtil.maskName(user.getName())); user.setEmail(SensitiveUtil.maskEmail(user.getEmail())); user.setBankCard(SensitiveUtil.maskBankCard(user.getBankCard())); return user;非常简单易懂数据脱敏不再是问题。但有些朋友又说了“每写一个接口或者每一个字段都要单独调用有点心累。有没有更方便的方案” 请看方案二。方案三自定义注解 Jackson利用Jackson的序列化机制在返回JSON时自动对标注了注解的字段进行脱敏。java体验AI代码助手代码解读复制代码// 脱敏注解 Retention(RetentionPolicy.RUNTIME) JacksonAnnotationsInside JsonSerialize(using SensitiveSerializer.class) public interface Sensitive { SensitiveType value(); } // 脱敏类型枚举 public enum SensitiveType { PHONE, // 手机号 ID_CARD, // 身份证 NAME, // 姓名 EMAIL, // 邮箱 BANK_CARD // 银行卡 } // 脱敏序列化器 public class SensitiveSerializer extends JsonSerializerString { private SensitiveType type; public SensitiveSerializer(SensitiveType type) { this.type type; } Override public void serialize(String value, JsonGenerator gen, SerializerProvider provider) throws IOException { String result; switch (type) { case PHONE: result SensitiveUtil.maskPhone(value); break; case ID_CARD: result SensitiveUtil.maskIdCard(value); break; case NAME: result SensitiveUtil.maskName(value); break; case EMAIL: result SensitiveUtil.maskEmail(value); break; case BANK_CARD: result SensitiveUtil.maskBankCard(value); break; default: result value; } gen.writeString(result); } }使用方式java体验AI代码助手代码解读复制代码public class User { private String name; Sensitive(SensitiveType.PHONE) private String phone; Sensitive(SensitiveType.ID_CARD) private String idCard; Sensitive(SensitiveType.NAME) private String name; Sensitive(SensitiveType.EMAIL) private String email; Sensitive(SensitiveType.BANK_CARD) private String bankCard; }性能好只在序列化时生效。配置一次全局都可以用不影响业务逻辑。缺点是需要配置Jackson。方案四Lombok 自定义Getter轻量级替代方案如果你不想引入太多框架但用了Lombok这个方案很合适。思路让Lombok不生成默认getter我们自己写一个带脱敏逻辑的getter。1. 关闭Lombok的默认getter去掉Data只保留你需要的Lombok注解手动添加带脱敏的getterjava体验AI代码助手代码解读复制代码Builder NoArgsConstructor AllArgsConstructor Setter public class UserVO { private String name; private String phone; private String idCard; private String email; private String bankCard; // 自定义脱敏 getter public String getName() { return SensitiveUtil.maskName(name); } public String getPhone() { return SensitiveUtil.maskPhone(phone); } public String getIdCard() { return SensitiveUtil.maskIdCard(idCard); } public String getEmail() { return SensitiveUtil.maskEmail(email); } public String getBankCard() { return SensitiveUtil.maskBankCard(bankCard); } }这样只要调用user.getxx()返回的就是脱敏后的数据。实现简单不依赖额外组件适合小项目。但每个字段都要手动写getter有点啰嗦了。方案五AOP切面深度脱敏推荐这个是主推的方案特别适合嵌套对象、List、Map等类型。 为什么因为前面的方法大多只处理单层DTO。但现实中经常是java体验AI代码助手代码解读复制代码public class User { Sensitive(SensitiveType.PHONE) private String phone; // 这个能脱敏 private UserDetail detail; // 这里面还有敏感字段 } public class UserDetail { Sensitive(SensitiveType.ID_CARD) private String idCard; // 这个脱敏不了 }返回User对象时phone字段能脱敏但detail.idCard还是明文显示解决方案用AOP 深度递归反射。详细步骤如下1. 定义脱敏类型枚举java体验AI代码助手代码解读复制代码/** * 脱敏类型枚举 */ public enum SensitiveType { PHONE, // 手机号 ID_CARD, // 身份证 NAME, // 姓名 EMAIL, // 邮箱 BANK_CARD // 银行卡 }2. 定义注解Sensitivejava体验AI代码助手代码解读复制代码/** * 脱敏注解 */ Target(ElementType.FIELD) Retention(RetentionPolicy.RUNTIME) public interface Sensitive { SensitiveType value(); }3. 需要保留方案一的工具类增加以下的maskByType方法java体验AI代码助手代码解读复制代码/** * 根据类型脱敏 */ public static String maskByType(String value, SensitiveType type) { if (StringUtils.isBlank(value)) { return value; } switch (type) { case PHONE: return maskPhone(value); case ID_CARD: return maskIdCard(value); case NAME: return maskName(value); case EMAIL: return maskEmail(value); case BANK_CARD: return maskBankCard(value); default: return value; } } }4. 写AOP切面核心逻辑java体验AI代码助手代码解读复制代码/** * 深度脱敏AOP处理器 */ Aspect Component Slf4j public class DeepSensitiveAspect { // 定义切点拦截Controller层所有方法 Pointcut(execution(* com.example.controller..*.*(..))) public void controllerPointcut() {} // 定义切点拦截Service层所有方法 Pointcut(execution(* com.example.service..*.*(..))) public void servicePointcut() {} /** * 环绕通知处理Controller层返回结果 */ Around(controllerPointcut()) public Object aroundController(ProceedingJoinPoint joinPoint) throws Throwable { Object result joinPoint.proceed(); return processDeepSensitive(result); } /** * 环绕通知处理Service层返回结果 */ Around(servicePointcut()) public Object aroundService(ProceedingJoinPoint joinPoint) throws Throwable { Object result joinPoint.proceed(); return processDeepSensitive(result); } /** * 深度脱敏处理 */ private Object processDeepSensitive(Object obj) { if (obj null) { return null; } // 处理集合类型 if (obj instanceof List) { return processList((List?) obj); } // 处理数组类型 if (obj.getClass().isArray()) { return processArray((Object[]) obj); } // 处理Map类型 if (obj instanceof Map) { return processMap((Map?, ?) obj); } // 处理分页对象Spring Data Page if (obj instanceof Page) { return processPage((Page?) obj); } // 处理普通Java对象 if (isCustomClass(obj.getClass())) { return processObject(obj); } // 基本类型直接返回 return obj; } /** * 处理List集合 */ private List? processList(List? list) { if (list null || list.isEmpty()) { return list; } return list.stream() .map(this::processDeepSensitive) .collect(Collectors.toList()); } /** * 处理数组 */ private Object[] processArray(Object[] array) { if (array null || array.length 0) { return array; } Object[] result new Object[array.length]; for (int i 0; i array.length; i) { result[i] processDeepSensitive(array[i]); } return result; } /** * 处理Map */ private Map?, ? processMap(Map?, ? map) { if (map null || map.isEmpty()) { return map; } MapObject, Object result new HashMap(); for (Map.Entry?, ? entry : map.entrySet()) { result.put(entry.getKey(), processDeepSensitive(entry.getValue())); } return result; } /** * 处理分页对象 */ private Page? processPage(Page? page) { if (page null) { return null; } List? content processList(page.getContent()); return new PageImpl(content, page.getPageable(), page.getTotalElements()); } /** * 处理单个对象 */ private Object processObject(Object obj) { if (obj null) { return null; } Class? clazz obj.getClass(); try { // 获取所有字段包括父类 ListField fields getAllFields(clazz); for (Field field : fields) { field.setAccessible(true); // 检查是否有脱敏注解 Sensitive sensitive field.getAnnotation(Sensitive.class); if (sensitive ! null field.getType() String.class) { // 处理敏感字段 processSensitiveField(obj, field, sensitive); } else { // 递归处理嵌套对象 processNestedField(obj, field); } } } catch (Exception e) { log.warn(脱敏处理失败: {}, e.getMessage()); } return obj; } /** * 处理敏感字段 */ private void processSensitiveField(Object obj, Field field, Sensitive sensitive) { try { String value (String) field.get(obj); if (StringUtils.isNotBlank(value)) { String maskedValue SensitiveUtil.maskByType(value, sensitive.value()); field.set(obj, maskedValue); } } catch (IllegalAccessException e) { log.warn(字段脱敏失败: {}, field.getName()); } } /** * 处理嵌套字段 */ private void processNestedField(Object obj, Field field) { try { Object fieldValue field.get(obj); if (fieldValue ! null isCustomClass(field.getType())) { // 递归处理嵌套对象 processObject(fieldValue); } } catch (IllegalAccessException e) { // 忽略无法访问的字段 } } /** * 获取所有字段包括父类 */ private ListField getAllFields(Class? clazz) { ListField fields new ArrayList(); while (clazz ! null clazz ! Object.class) { fields.addAll(Arrays.asList(clazz.getDeclaredFields())); clazz clazz.getSuperclass(); } return fields; } /** * 判断是否为自定义类非JDK类 */ private boolean isCustomClass(Class? clazz) { return clazz ! null !clazz.isPrimitive() !clazz.getName().startsWith(java.) !clazz.getName().startsWith(javax.); } }5. 实体类使用示例java体验AI代码助手代码解读复制代码/** * 用户实体 */ Data public class User { private Long id; Sensitive(SensitiveType.NAME) private String name; Sensitive(SensitiveType.PHONE) private String phone; Sensitive(SensitiveType.ID_CARD) private String idCard; Sensitive(SensitiveType.EMAIL) private String email; Sensitive(SensitiveType.BANK_CARD) private String bankCard; // 嵌套对象也会被处理 private UserDetail detail; } /** * 用户详情实体 */ Data public class UserDetail { Sensitive(SensitiveType.PHONE) private String emergencyPhone; Sensitive(SensitiveType.ID_CARD) private String spouseIdCard; }6. Controller使用示例java体验AI代码助手代码解读复制代码RestController RequestMapping(/users) public class UserController { Autowired private UserService userService; /** * 返回单个用户自动脱敏 */ GetMapping(/{id}) public User getUser(PathVariable Long id) { return userService.getUserById(id); } /** * 返回用户列表自动脱敏 */ GetMapping public ListUser getUsers() { return userService.getAllUsers(); } /** * 返回分页数据自动脱敏 */ GetMapping(/page) public PageUser getUsersByPage(Pageable pageable) { return userService.getUsersByPage(pageable); } }7. 效果验证json体验AI代码助手代码解读复制代码{ id: 1, name: 张*四, phone: 138****5678, idCard: 430101********8888, email: 1234****qq.com, bankCard: 6230********2405, detail: { emergencyPhone: 139****8765, spouseIdCard: 110105********1234 } }优点全面支持手机号、身份证、姓名、邮箱、银行卡全搞定深度处理支持多层嵌套对象脱敏零侵入业务代码无需任何修改高性能使用反射缓存性能优化易扩展新增脱敏类型只需扩展枚举和工具方法方案六Mysql数据库层脱敏最简单的MySQL脱敏SQLsql体验AI代码助手代码解读复制代码SELECT id, CONCAT(LEFT(phone, 3), ****, RIGHT(phone, 4)) AS phone, CONCAT(LEFT(id_card, 6), ********, RIGHT(id_card, 4)) AS id_card, CONCAT(LEFT(name, 1), *, RIGHT(name, 1)) AS name, CONCAT(LEFT(email, 4), ****, SUBSTRING(email, LOCATE(, email))) AS email, CONCAT(LEFT(bank_card, 4), ********, RIGHT(bank_card, 4)) AS bank_card FROM users;分字段详细写法1. 手机号脱敏sql体验AI代码助手代码解读复制代码SELECT CONCAT(LEFT(phone, 3), ****, RIGHT(phone, 4)) AS phone FROM users; -- 13812345678 → 138****56782. 身份证脱敏sql体验AI代码助手代码解读复制代码SELECT CONCAT(LEFT(id_card, 6), ********, RIGHT(id_card, 4)) AS id_card FROM users; -- 430101199003078888 → 430101********88883. 姓名脱敏sql体验AI代码助手代码解读复制代码SELECT CASE WHEN LENGTH(name) 1 THEN * WHEN LENGTH(name) 2 THEN CONCAT(LEFT(name, 1), *) ELSE CONCAT(LEFT(name, 1), *, RIGHT(name, 1)) END AS name FROM users; -- 张三四 → 张*四4. 邮箱脱敏sql体验AI代码助手代码解读复制代码SELECT CONCAT(LEFT(email, 4), ****, SUBSTRING(email, LOCATE(, email))) AS email FROM users; -- 12345678qq.com → 1234****qq.com5. 银行卡脱敏sql体验AI代码助手代码解读复制代码SELECT CONCAT(LEFT(bank_card, 4), ********, RIGHT(bank_card, 4)) AS bank_card FROM users; -- 6230351888852405 → 6230********24056. 完整查询示例sql体验AI代码助手代码解读复制代码-- 查询单个用户 SELECT id, CONCAT(LEFT(phone, 3), ****, RIGHT(phone, 4)) AS phone, CONCAT(LEFT(id_card, 6), ********, RIGHT(id_card, 4)) AS id_card, CASE WHEN LENGTH(name) 1 THEN * WHEN LENGTH(name) 2 THEN CONCAT(LEFT(name, 1), *) ELSE CONCAT(LEFT(name, 1), *, RIGHT(name, 1)) END AS name, CONCAT(LEFT(email, 4), ****, SUBSTRING(email, LOCATE(, email))) AS email, CONCAT(LEFT(bank_card, 4), ********, RIGHT(bank_card, 4)) AS bank_card FROM users WHERE id 1; -- 查询用户列表 SELECT id, CONCAT(LEFT(phone, 3), ****, RIGHT(phone, 4)) AS phone, CONCAT(LEFT(name, 1), *, RIGHT(name, 1)) AS name FROM users ORDER BY id DESC LIMIT 10;7. 创建视图方案如果经常要用建个视图更方便sql体验AI代码助手代码解读复制代码CREATE VIEW v_user_masked AS SELECT id, CONCAT(LEFT(phone, 3), ****, RIGHT(phone, 4)) AS phone, CONCAT(LEFT(id_card, 6), ********, RIGHT(id_card, 4)) AS id_card, CASE WHEN LENGTH(name) 1 THEN * WHEN LENGTH(name) 2 THEN CONCAT(LEFT(name, 1), *) ELSE CONCAT(LEFT(name, 1), *, RIGHT(name, 1)) END AS name, CONCAT(LEFT(email, 4), ****, SUBSTRING(email, LOCATE(, email))) AS email, CONCAT(LEFT(bank_card, 4), ********, RIGHT(bank_card, 4)) AS bank_card FROM users; -- 直接用视图查询 SELECT * FROM v_user_masked WHERE id 1;8. 联表查询脱敏sql体验AI代码助手代码解读复制代码SELECT u.id, CONCAT(LEFT(u.name, 1), *, RIGHT(u.name, 1)) AS user_name, CONCAT(LEFT(u.phone, 3), ****, RIGHT(u.phone, 4)) AS user_phone, o.order_no, o.amount FROM users u INNER JOIN orders o ON u.id o.user_id;最后总结方案优点缺点适用场景Hutool 工具库开箱即用代码简洁功能全依赖第三方库灵活性有限快速开发、小项目、原型阶段正则工具类 手动调用简单直接无额外依赖需手动调用侵入业务代码单字段少量脱敏轻量级应用自定义注解 Jackson 序列化自动化序列化脱敏性能好仅作用于 JSON 输出不支持嵌套对象深层脱敏REST API 返回数据脱敏Lombok 自定义 Getter轻量无需切面或框架每个字段都要写 getter重复代码多VO/DTO 层控制输出适合简单结构AOP 切面深度脱敏推荐支持嵌套对象、集合、分页零侵入业务可全局生效易扩展实现复杂需理解反射和 AOP中大型项目复杂数据结构统一脱敏治理MySQL 数据库层脱敏不依赖 Java 层查询即脱敏安全隔离SQL 复杂维护成本高无法动态控制报表查询、只读视图、DBA 视角脱敏最佳实践建议1.如果你是新手或者小项目起步方案一Hutool或方案二工具类。2.如果你做的是标准后端服务API推荐方案三Jackson 注解序列化结合Sensitive注解在返回 JSON时自动脱敏。3.如果你的项目结构复杂、嵌套深、List/Map/分页多强烈推荐方案五AOP深度脱敏这是真正意义上的“一次配置处处脱敏”。4.如果你有DB权限且需要给前端/报表提供固定脱敏视图使用方案六MySQL脱敏视图实现数据访问层面的安全隔离。希望这篇文章能帮你在实际项目中轻松搞定数据脱敏问题。如果觉得有用欢迎点赞、收藏、转发让更多人看到