很多业务数据在展示上需要进行脱敏处理,保护重要的敏感信息。如电话号码脱敏,期望展示的数据格式是156****7837;如身份证号码脱敏,期望展示的数据格式是420***********113X。
当然在记录操作日志时对密码等信息进行过滤,保证其安全。那么可以采用Fastjson进行配置(本文所用的是SpringBoot环境,采用的fastjson的版本是1.2.83),步骤如下:
1)定义枚举类,列举所有需要脱敏的字段类型
package com.zxh.test.enums; /** * @author zhongyushi */ public enum DesensitionTypeEnum { /** * 中文名 */ CHINA_NAME, /** * 16或者18身份证号 */ ID_CARD, /** * 11位手机号 */ MOBILE_PHONE, /** * 固定电话 */ FIXED_PHONE, /** * 银行卡号 */ BANK_CARD, /** * 电子邮件 */ EMAIL, /** * 密码 */ PASSWORD, /** * 车牌号 */ CAR_NUMBER, /** * 地址 */ ADDRESS, }
2)定义注解,用来标注需要的字段
package com.zxh.test.annotation; import com.zxh.test.enums.DesensitionTypeEnum; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * 数据脱敏注解 * * @author zhongyushi */ @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface Desensitization { /** * 脱敏规则类型 * * @return */ DesensitionTypeEnum value(); }
3)添加脱敏的工具类
package com.zxh.test.util; import com.alibaba.fastjson.support.spring.PropertyPreFilters; import org.apache.commons.lang.StringUtils; /** * 数据脱敏工具类 */public class DesensitizedUtils { /** * 【中文姓名】只显示第一个汉字,其他隐藏为2个星号,比如:李** * * @param fullName * @return */ public static String chineseName(String fullName) { if (StringUtils.isBlank(fullName)) { return ""; } String name = StringUtils.left(fullName, 1); return StringUtils.rightPad(name, StringUtils.length(fullName), "*"); } /** * 【身份证号】前三位 和后四位 * * @param idCardNum * @return */ public static String idCardNum(String idCardNum) { return idCardNum(idCardNum, 3, 4); } /** * 【身份证号】前三位 和后四位 * * @param front * @param end * @return */ public static String idCardNum(String idCardNum, int front, int end) { //身份证不能为空 if (StringUtils.isEmpty(idCardNum)) { return ""; } //需要截取的长度不能大于身份证号长度 if ((front + end) > idCardNum.length()) { return ""; } //需要截取的不能小于0 if (front < 0 || end < 0) { return ""; } //计算*的数量 int asteriskCount = idCardNum.length() - (front + end); StringBuffer asteriskStr = new StringBuffer(); for (int i = 0; i < asteriskCount; i++) { asteriskStr.append("*"); } String regex = "(\\w{" + String.valueOf(front) + "})(\\w+)(\\w{" + String.valueOf(end) + "})"; return idCardNum.replaceAll(regex, "$1" + asteriskStr + "$3"); } /** * 【固定电话】 前四位,后两位 * * @param num * @return */ public static String fixedPhone(String num) { if (StringUtils.isBlank(num)) { return ""; } return StringUtils.left(num, 4).concat(StringUtils.removeStart(StringUtils.leftPad(StringUtils.right(num, 2), StringUtils.length(num), "*"), "****")); } /** * 【手机号码】前三位,后四位,其他隐藏,比如135****4310 * * @param num * @return */ public static String mobilePhone(String num) { if (StringUtils.isBlank(num)) { return ""; } return StringUtils.left(num, 3).concat(StringUtils.removeStart(StringUtils.leftPad(StringUtils.right(num, 4), StringUtils.length(num), "*"), "***")); } /** * 【地址】只显示到地区,不显示详细地址,比如:北京市海淀区**** * * @param address * @param sensitiveSize 敏感信息长度 * @return */ public static String address(String address, int sensitiveSize) { if (StringUtils.isBlank(address)) { return ""; } int length = StringUtils.length(address); return StringUtils.rightPad(StringUtils.left(address, length - sensitiveSize), length, "*"); } /** * 【电子邮箱】邮箱前缀仅显示第一个字母,前缀其他隐藏,用星号代替,@及后面的地址显示,比如:d**@126.com> * * @param email * @return */ public static String email(String email) { if (StringUtils.isBlank(email)) { return ""; } int index = StringUtils.indexOf(email, "@"); if (index <= 1) { return email; } else { return StringUtils.rightPad(StringUtils.left(email, 1), index, "*").concat(StringUtils.mid(email, index, StringUtils.length(email))); } } /** * 【银行卡号】前六位,后四位,其他用星号隐藏每位1个星号,比如:6222600**********1234 * * @param cardNum * @return */ public static String bankCard(String cardNum) { if (StringUtils.isBlank(cardNum)) { return ""; } return StringUtils.left(cardNum, 6).concat(StringUtils.removeStart(StringUtils.leftPad(StringUtils.right(cardNum, 4), StringUtils.length(cardNum), "*"), "******")); } /** * 【密码】密码的全部字符都用*代替,比如:****** * * @param password * @return */ public static String password(String password) { if (StringUtils.isBlank(password)) { return ""; } String pwd = StringUtils.left(password, 0); return StringUtils.rightPad(pwd, StringUtils.length(password), "*"); } /** * 【车牌号】前两位后一位,比如:苏M****5 * * @param carNumber * @return */ public static String carNumber(String carNumber) { if (StringUtils.isBlank(carNumber)) { return ""; } return StringUtils.left(carNumber, 2). concat(StringUtils.removeStart(StringUtils.leftPad(StringUtils.right(carNumber, 1), StringUtils.length(carNumber), "*"), "**")); } /** * 忽略敏感属性,即过滤掉不显示 * * @param propertyName 要忽略的字段名称 * @return */ public static PropertyPreFilters.MySimplePropertyPreFilter excludePropertyPreFilter(String... propertyName) { return new PropertyPreFilters().addFilter().addExcludes(propertyName); } }
4)新建过滤器实现VueFilter,对字段进行过滤
package com.zxh.test.filter; import com.alibaba.fastjson.serializer.ValueFilter; import com.zxh.test.annotation.Desensitization; import com.zxh.test.enums.DesensitionTypeEnum; import com.zxh.test.util.DesensitizedUtils; import lombok.extern.slf4j.Slf4j; import java.lang.reflect.Field; /** * fastjson序列化过滤器 * * @author zhongyushi */ @Slf4j public class ValueDesensitizeFilter implements ValueFilter { @Override public Object process(Object object, String name, Object value) { if (null == value || !(value instanceof String) || ((String) value).length() == 0) { return value; } try { Desensitization annotation; //获取字段 Field field = object.getClass().getDeclaredField(name); if (String.class != field.getType() || (annotation = field.getAnnotation(Desensitization.class)) == null) { return value; } //获取字段中注解的类型 DesensitionTypeEnum typeEnum = annotation.value(); String body = (String) value; switch (typeEnum) { case CHINA_NAME: value = DesensitizedUtils.chineseName(body); break; case ID_CARD: value = DesensitizedUtils.idCardNum(body); break; case MOBILE_PHONE: value = DesensitizedUtils.mobilePhone(body); break; case FIXED_PHONE: value = DesensitizedUtils.fixedPhone(body); break; case BANK_CARD: value = DesensitizedUtils.bankCard(body); break; case EMAIL: value = DesensitizedUtils.email(body); break; case PASSWORD: value = DesensitizedUtils.password(body); break; case CAR_NUMBER: value = DesensitizedUtils.carNumber(body); break; case ADDRESS: value = DesensitizedUtils.address(body, 6); break; } } catch (NoSuchFieldException e) { log.error("通过反射获取字段异常:{}", e); } return value; } }
5)自定义消息转换对象,实现数据脱敏
package com.zxh.test.config; import com.alibaba.fastjson.serializer.SerializerFeature; import com.alibaba.fastjson.support.config.FastJsonConfig; import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter; import com.zxh.test.filter.ValueDesensitizeFilter; import org.springframework.boot.autoconfigure.http.HttpMessageConverters; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.http.MediaType; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import java.util.ArrayList; import java.util.List; /** * 自定义消息转换对象 * * @author zhongyushi */ @Configuration public class FastJsonWebSerializationConfiguration implements WebMvcConfigurer { @Bean(name = "httpMessageConverters") public HttpMessageConverters fastJsonHttpMessageConverters() { // 1.定义一个converters转换消息的对象 FastJsonHttpMessageConverter fastConverter = new FastJsonHttpMessageConverter(); // 2.添加fastjson的配置信息,比如: 是否需要格式化返回的json数据 FastJsonConfig fastJsonConfig = new FastJsonConfig(); fastJsonConfig.setSerializerFeatures(SerializerFeature.PrettyFormat); // 中文乱码解决方案 List<MediaType> mediaTypes = new ArrayList<>(); //设定json格式且编码为UTF-8 mediaTypes.add(MediaType.APPLICATION_JSON_UTF8); fastConverter.setSupportedMediaTypes(mediaTypes); //添加自己写的拦截器,实现脱敏 fastJsonConfig.setSerializeFilters(new ValueDesensitizeFilter()); // 3.在converter中添加配置信息 fastConverter.setFastJsonConfig(fastJsonConfig); // 4.将converter赋值给HttpMessageConverter HttpMessageConverter<?> converter = fastConverter; // 5.返回HttpMessageConverters对象 return new HttpMessageConverters(converter); } }
6)新建实体类,使用注解
package com.zxh.test; import com.zxh.test.annotation.Desensitization; import com.zxh.test.enums.DesensitionTypeEnum; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; @Data @NoArgsConstructor @AllArgsConstructor public class UserVo { //id主键 private String id; //中文名 @Desensitization(DesensitionTypeEnum.CHINA_NAME) private String chineseName; //密码 @Desensitization(DesensitionTypeEnum.PASSWORD) private String password; //确认密码 private String passwd; //手机号 @Desensitization(DesensitionTypeEnum.MOBILE_PHONE) private String mobilePhone; //固定电话 @Desensitization(DesensitionTypeEnum.FIXED_PHONE) private String fixedPhone; //身份证号码 @Desensitization(DesensitionTypeEnum.ID_CARD) private String idCard; //地址 @Desensitization(DesensitionTypeEnum.ADDRESS) private String address; //邮箱 @Desensitization(DesensitionTypeEnum.EMAIL) private String email; //银行卡号 @Desensitization(DesensitionTypeEnum.BANK_CARD) private String bankCard; //车牌号 @Desensitization(DesensitionTypeEnum.CAR_NUMBER) private String carNumber; }
7)新建controller接口,设置对象属性值
package com.zxh.test.controller; import com.zxh.test.UserVo; import lombok.extern.slf4j.Slf4j; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/api/user") @Slf4j public class UserController { @GetMapping("/set") public UserVo set() { UserVo userVo = new UserVo(); userVo.setId("3"); userVo.setMobilePhone("15623347837"); userVo.setIdCard("42032119990909113X"); userVo.setAddress("湖北省武汉市洪山区光谷大道1号"); userVo.setCarNumber("鄂W6669S"); userVo.setBankCard("6212267877021329"); userVo.setPassword("wwwbaidu999@"); userVo.setPasswd("wwwbaidu999@"); userVo.setChineseName("钟小嘿"); userVo.setEmail("[email protected]"); userVo.setFixedPhone("027-82829991"); return userVo; } }
这里返回的是实体对象,实际上返回包含此对象的集合或其他类型,都是可以的。
8)启动项目,在浏览器访问后返回结果如下
此时已基本实现了敏感数据的脱敏。而对于非controller的数据,需要手动转换进行脱敏,另外可过滤掉不需要的字段,如以下的测试方法
@Test public void test3() { //非controller调用序列化脱敏.手动进行转换 UserVo userVo = new UserVo(); userVo.setId("3"); userVo.setMobilePhone("15623347837"); userVo.setIdCard("42032119990909113X"); userVo.setAddress("湖北省武汉市洪山区光谷大道1号"); userVo.setCarNumber("鄂W6669S"); userVo.setBankCard("6212267877021329"); userVo.setPassword("wwwbaidu999@"); userVo.setPasswd("wwwbaidu999@"); userVo.setChineseName("钟小嘿"); userVo.setEmail("[email protected]"); userVo.setFixedPhone("027-82829991"); //同时指定了两个过滤器,分别对数据进行格式化和字段的过滤(假设去掉字段passwd) SerializeFilter[] filters = {new ValueDesensitizeFilter(), DesensitizedUtils.excludePropertyPreFilter( "passwd")}; System.out.println(JSON.toJSONString(userVo, filters)); }
打印的结果经json格式化后如下:
可以看出已实现了数据脱敏和字段过滤。
标签:Fastjson,return,String,userVo,com,import,数据,StringUtils,脱敏 From: https://www.cnblogs.com/zys2019/p/17014573.html