很多业务数据在展示上需要进行脱敏处理,保护重要的敏感信息。如电话号码脱敏,期望展示的数据格式是156****7837;如身份证号码脱敏,期望展示的数据格式是420***********113X。

当然在记录操作日志时对密码等信息进行过滤,保证其安全。那么可以采用Fastjson进行配置(本文所用的是SpringBoot环境)

1.Fastjson实现数据脱敏

采用的fastjson的版本是1.2.83,依赖:

<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.83</version>
</dependency>

后续步骤如下:

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("zhongxiaohei@123.com");
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("zhongxiaohei@123.com");
userVo.setFixedPhone("027-82829991");
//同时指定了两个过滤器,分别对数据进行格式化和字段的过滤(假设去掉字段passwd)
SerializeFilter[] filters = {new ValueDesensitizeFilter(), DesensitizedUtils.excludePropertyPreFilter( "passwd")};
System.out.println(JSON.toJSONString(userVo, filters));
}

打印的结果经json格式化后如下:

可以看出已实现了数据脱敏和字段过滤。

2.Fastjson2实现数据脱敏

Fastjson2与Fastjson实现脱敏的方式相同,只是Fastjson2的有些类做了调整,只说明其差异的部分。

1)依赖的不同

除了要导入Fastjson2外,还需要导入其扩展包

<dependency>
<groupId>com.alibaba.fastjson2</groupId>
<artifactId>fastjson2</artifactId>
<version>2.0.21</version>
</dependency>
<dependency>
<groupId>com.alibaba.fastjson2</groupId>
<artifactId>fastjson2-extension</artifactId>
<version>2.0.21</version>
</dependency>

2)自定义消息转换对象的方式不同

    @Bean(name = "httpMessageConverters")
public HttpMessageConverters fastJsonHttpMessageConverters() {
// 1.定义一个converters转换消息的对象
FastJsonHttpMessageConverter fastConverter = new FastJsonHttpMessageConverter();
// 2.添加fastjson的配置信息,比如: 是否需要格式化返回的json数据
FastJsonConfig fastJsonConfig = new FastJsonConfig();
// 中文乱码解决方案
List<MediaType> mediaTypes = new ArrayList<>();
//设定json格式且编码为UTF-8
mediaTypes.add(MediaType.APPLICATION_JSON_UTF8);
fastConverter.setSupportedMediaTypes(mediaTypes);
//添加自己写的拦截器,实现脱敏
fastJsonConfig.setWriterFilters(new ValueDesensitizeFilter());
// 3.在converter中添加配置信息
fastConverter.setFastJsonConfig(fastJsonConfig);
// 4.将converter赋值给HttpMessageConverter
HttpMessageConverter<?> converter = fastConverter;
// 5.返回HttpMessageConverters对象
return new HttpMessageConverters(converter);
}

不再设置序列化的类型,另外,过滤器也配置在了writeFilters中。另外,包名发生了变化

import com.alibaba.fastjson2.support.config.FastJsonConfig;
import com.alibaba.fastjson2.support.spring.http.converter.FastJsonHttpMessageConverter;

3)没有PropertyPreFilters类,因此无法实现忽略某些字段。

Fastjson之数据脱敏的更多相关文章

  1. java 数据脱敏

    所谓数据脱敏是指对某些敏感信息通过脱敏规则进行数据的变形,实现敏感隐私数据的可靠保护.在涉及客户安全数据或者一些商业性敏感数据的情况下,在不违反系统规则条件下,对真实数据进行改造并提供测试使用,如身份 ...

  2. Oracle汉字用户名数据脱敏长度不变,rpad函数使用

    信息安全考虑,有时需要对用户名称进行数据脱敏. 针对Oracle数据库,进行取数数据脱敏处理 脱敏规则: 长度小于9个字符,只保留前3个汉字与后3个汉字,中间全部由*填充. 长度9个字及以上及奇数,隐 ...

  3. php 数据脱敏显示

    /** * 数据脱敏 * @param $string 需要脱敏值 * @param int $start 开始 * @param int $length 结束 * @param string $re ...

  4. Android商城开发系列(六)——使用 OkHttpUtils 请求网络 + 使用 fastjson解析数据

    OkHttp是Google推荐使用的一个开源的网络请求框架,Android开发中涉及到网络请求和接口调用现在大部分都是使用OkHttp,网上已经有不少人针对OkHttp进行了封装,这里推荐一下鸿洋大神 ...

  5. 如何用java实现数据脱敏

    数据脱敏是什么意思呢? 数据脱敏是指对某些敏感信息通过脱敏规则进行数据的变形,实现敏感隐私数据的可靠保护.在涉及客户安全数据或者一些商业性敏感数据的情况下,在不违反系统规则条件下,对真实数据进行改造并 ...

  6. Oracle 11g数据脱敏

    Oracle 11g数据脱敏 前言 最近开发人员有个需求,导一份生产库的数据到测试库. 由于生产数据安全需要,需要并允许对导出的数据进行加密脱敏处理. 关于加密和脱敏 个人理解, 加密是通过一系列规则 ...

  7. Springboot 配置文件、隐私数据脱敏的最佳实践(原理+源码)

    大家好!我是小富- 这几天公司在排查内部数据账号泄漏,原因是发现某些实习生小可爱居然连带着账号.密码将源码私传到GitHub上,导致核心数据外漏,孩子还是没挨过社会毒打,这种事的后果可大可小. 说起这 ...

  8. 利用Jackson序列化实现数据脱敏

    几天前使用了Jackson对数据的自定义序列化.突发灵感,利用此方法来简单实现接口返回数据脱敏,故写此文记录. 核心思想是利用Jackson的StdSerializer,@JsonSerialize, ...

  9. 数据脱敏 t-closeness介绍与实现

    数据脱敏 t-closeness介绍与实现 本文主要基于t-closeness的首次提出团队Ninghui Li, Tiancheng Li, Suresh Venkatasubramanian发表的 ...

  10. ShardingJdbc-分表;分库;分库分表;读写分离;一主多从+分表;一主多从+分库分表;公共表;数据脱敏;分布式事务

    目录 创建项目 分表 导包 表结构 Yml 分库 Yml Java 分库分表 数据库 Yml 读写分离 数据库 Yml 其他 只请求主库 读写分离判断逻辑代码 一主多从+分表 Yml 一主多从+分库分 ...

随机推荐

  1. Cursor——Tab 标签:智能代码补全的终极工具

    引言 在现代软件开发中,代码自动补全功能已经成为提高开发效率的重要工具.Cursor 编辑器中的 Tab 标签功能通过先进的 AI 技术,将传统的代码补全提升到了一个全新的水平.它不仅提供基础的代码建 ...

  2. RestCloud企业级API网关,构建统一的API管理平台

    RestCloud企业级API网关由API网关完成各种协议的路由透传功能,再配合API服务编排平台和消息中间件模块即可完全替换原来笨重且为单体架构的ESB企业服务总线系统.RestCloud企业级AP ...

  3. nano文本编辑器使用方法

    网上大部分 Linux 相关教程在涉及文本编辑操作时都是选择的 Vim 编辑器,对于新手来说如何退出成了最大的难题.其实除了 Vim 之外还有别的选择,那就是 nano .上手 nano 几乎是零学习 ...

  4. SciTech-Mathematics-Probability+Statistics- Pandas DataFrame Histogram/BarChart/Boxplot/Scatterplot + Relative Frequency Histogram: Definition + Example()

    Links: How to Plot Multiple Series from a Pandas DataFrame How to Make a Scatterplot From a Pandas D ...

  5. CloudQuery v1.4.0 发布 | 全栈数据管控平台的新起点

    Hello,社区的小伙伴们,好久不见!经过一个多月努力,CloudQuery v1.4.0 终于和大家见面了! v1.4.0 版本的新功能包括:调度中心模块.DTS (Database Tools S ...

  6. 读《我加载了恋爱游戏》(by掠过的乌鸦)

    读书经过 事情发生于寒假请假在家的一个下午,闲来无事刷b站,看到up主凌凌上将带老爸看小说女主的视频(因为<我真没想重生啊>点进去的),出现清野凛.然后由于推送机制加上之前也被推过,我就看 ...

  7. ROS1(20.04 noetic) + PX4 + AirSim

    博客地址:https://www.cnblogs.com/zylyehuo/ 参考视频: 保姆级教程~手把手教你实现 ROS2 + PX4 + AirSim 联动 博主使用的版本配置如下: ROS: ...

  8. IDEA激活付费插件ANSI Highlighter Premium永久激活教程(2025最新版)

    大家好,欢迎来到程序视点!我是你们的老朋友.安戈! IDEA付费插件TOP1 关注过[程序视点]的读者小伙伴们肯定都知道,我们付费版JetBrains全家桶IDE是支持常用付费插件的!!       ...

  9. 云原生环境中的镜像兼容性(NFD项目)

    在电信.高性能或 AI 计算等必须高度可靠且满足严格性能标准的行业中,容器化应用通常需要特定的操作系统配置或硬件支持. 通常的做法是要求使用特定版本的内核.其配置.设备驱动程序或系统组件. 尽管存在开 ...

  10. 【2025】webstorm安装教程及永久激活图解(附安装包+永久使用方法)

    一.webstorm介绍 JetBrains开发的‌专业级JavaScript/TypeScript集成开发环境‌,被开发者誉为"前端开发神器"与"最智能的HTML5编辑 ...