首页 > 其他分享 >Fastjson之数据脱敏

Fastjson之数据脱敏

时间:2023-01-06 10:46:48浏览次数:35  
标签:Fastjson return String userVo com import 数据 StringUtils 脱敏

很多业务数据在展示上需要进行脱敏处理,保护重要的敏感信息。如电话号码脱敏,期望展示的数据格式是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

相关文章

  • 火山引擎DataLeap数据调度实例的 DAG 优化方案
    更多技术交流、求职机会,欢迎关注字节跳动数据平台微信公众号,并进入官方交流群实例DAG介绍DataLeap是火山引擎自研的一站式大数据中台解决方案,集数据集成、开发、运......
  • torch的数据集
    torch.utils.data.TensorDataset这个类可以初始化数据集例子:import torchfrom torch.utils import data# torch.utils.data.dataset 类的使用x = torch.ar......
  • 财务数据运算量太大了,系统老崩怎么办?
    相信不少的小伙伴都遇到过这样的情况:数据该清洗的清洗,该整理的整理,却在进行运算分析时,系统崩了!特别是在做运算组合多变又复杂的财务数据运算时,系统一旦崩溃,整个工作就得归零......
  • 火山引擎DataLeap数据调度实例的 DAG 优化方案
    更多技术交流、求职机会,欢迎关注字节跳动数据平台微信公众号,并进入官方交流群实例DAG介绍DataLeap是火山引擎自研的一站式大数据中台解决方案,集数据集成、开发、运维、治......
  • 技术汇总:第十五章:MyBatisGenerator数据层代码生成
    第一步:pom.xml<dependencies><dependency><groupId>com.macro.mall</groupId><artifactId>mall-common</artifactId......
  • 企业为什么要利用数据中台进行数字化转型?_光点科技
    近两年“数字化”已经悄悄的替代了“信息化”。那么什么是“企业的数字化转型”?数字化转型是企业战略层面的概念,它并不是追求眼前效益的机灵战术,其本质,是用数字化技术对业务......
  • C#下ArcSDE(Oracle)图层数据变化监控实现
    因业务需要对ArcSDE中单个图层的变化进行监控,在出现图形插入、删除,图形和属性修改时及时获取到发生变化的要素。研究了一下可以通过C#进行监控。核心代码如下:stringsq......
  • mysql数据库系统复习--b站视频
    转自:https://www.jianshu.com/writer#/notebooks/41524309/notes/73440255 参考:https://www.bilibili.com/video/BV1S4411u74E?p=3 //b站视频1)登录别人的mysql:mysq......
  • 数据结构:ST表 学习笔记
    ST表RMQ问题RMQ是英文RangeMaximum/MinimumQuery的缩写,表示区间最大(最小)值。ST表是用于解决离线RMQ问题的一种线性数据结构,在全国青少年信息学奥林匹克系列竞......
  • Android笔记--Android studio里面打开数据库详解
    1、下载DatabaseNavigator插件,然后需要重启Androidstudio2、然后会总界面这里。出现这样一个图标然后选中DatabaseBrower:3、弹出这样一个界面然后点击绿色+号,选......