Rumah  >  Artikel  >  Java  >  Bagaimana untuk menambah fungsi penyahpekaan dalam SpringBoot

Bagaimana untuk menambah fungsi penyahpekaan dalam SpringBoot

WBOY
WBOYke hadapan
2023-05-23 21:04:041149semak imbas

Fungsi penyahpekaan baharu dalam projek SpringBoot

Latar belakang projek

Projek SpringBoot sedang dibangunkan, yang mempunyai klien Web dan applet WeChat. Terminal web disediakan untuk digunakan oleh kakitangan, dan applet WeChat disediakan untuk orang ramai membuat tempahan. Sesetengah data sensitif dalam projek perlu dinyahpekakan dan dihantar ke applet WeChat untuk tontonan umum.

Penerangan Keperluan Projek

Dalam projek, memandangkan terdapat dua pengguna, kebenaran data untuk kedua-dua hujungnya tidak sama. Semua data boleh dilihat di bahagian web, manakala hanya data yang tidak sensitif boleh dilihat pada program mini.

Perlu membangunkan 通用脱敏功能

  • operasi penyahpekaan manual

  • menyokong berbilang objek,

  • Menyokong medan yang berbeza dan menyahpeka medan yang ditentukan

  • Penyahpekaan medan adalah pelbagai

  • Penyahpekaan medan Kaedah sensitif boleh disesuaikan

Penyelesaian projek

1 Penyelesaian

Gunakan注解方式 untuk menyokong medan tertentu dan medan yang berbeza dan keupayaan untuk melepaskan diri daripada objek.
Gunakan objek alat dan lulus parameter generik untuk menyokong operasi penyahpekaan pada objek berbeza.

2. Kod pelaksanaan

2.1 Anotasi Sensitif
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 自定义数据脱敏
 *
 * 例如: 身份证,手机号等信息进行模糊处理
 *
 * @author lzddddd
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Sensitive {

    /**
     * 脱敏数据类型
     */
    SensitiveType type() default SensitiveType.CUSTOMER;

    /**
     * 前置不需要打码的长度
     */
    int prefixNoMaskLen() default 0;

    /**
     * 后置不需要打码的长度
     */
    int suffixNoMaskLen() default 0;

    /**
     * 用什么打码
     */
    String symbol() default "*";

}
2.1 Penyahpekaan jenis penghitungan SensitiveType
public enum SensitiveType {

    /**
     * 自定义
     */
    CUSTOMER,
    /**
     * 名称
     **/
    CHINESE_NAME,
    /**
     * 身份证证件号
     **/
    ID_CARD_NUM,
    /**
     * 手机号
     **/
    MOBILE_PHONE,
    /**
     * 固定电话
     */
    FIXED_PHONE,
    /**
     * 密码
     **/
    PASSWORD,
    /**
     * 银行卡号
     */
    BANKCARD,
    /**
     * 邮箱
     */
    EMAIL,
    /**
     * 地址
     */
    ADDRESS,

}
2.3 PenyahpekaanAlat Penyahpekaan Contoh penggunaan

3.1 Objek yang perlu diberi anotasi

import com.ruoyi.common.annotation.Sensitive;
import com.ruoyi.common.constant.HttpStatus;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.enums.SensitiveType;
import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.Field;
import java.util.*;

@Slf4j
public class DesensitizedUtils<T> {

    /**
     * 脱敏数据列表
     */
    private List<T> list;

    /**
     * 注解列表
     */
    private List<Object[]> fields;

    /**
     * 实体对象
     */
    public Class<T> clazz;


    public DesensitizedUtils(Class<T> clazz)
    {
        this.clazz = clazz;
    }

    /**
     * 初始化数据
     *
     * @param list 需要处理数据
     */
    public void init(List<T> list){
        if (list == null)
        {
            list = new ArrayList<T>();
        }
        this.list = list;

        // 得到所有定义字段
        createSensitiveField();
    }

    /**
     * 初始化数据
     *
     * @param t 需要处理数据
     */
    public void init(T t){

        list = new ArrayList<T>();

        if (t != null)
        {
         list.add(t);
        }

        // 得到所有定义字段
        createSensitiveField();
    }

    /**
     * 得到所有定义字段
     */
    private void createSensitiveField()
    {
        this.fields = new ArrayList<Object[]>();
        List<Field> tempFields = new ArrayList<>();
        tempFields.addAll(Arrays.asList(clazz.getSuperclass().getDeclaredFields()));
        tempFields.addAll(Arrays.asList(clazz.getDeclaredFields()));
        for (Field field : tempFields)
        {
            // 单注解
            if (field.isAnnotationPresent(Sensitive.class))
            {
                putToField(field, field.getAnnotation(Sensitive.class));
            }

            // 多注解
//            if (field.isAnnotationPresent(Excels.class))
//            {
//                Excels attrs = field.getAnnotation(Excels.class);
//                Excel[] excels = attrs.value();
//                for (Excel excel : excels)
//                {
//                    putToField(field, excel);
//                }
//            }
        }
    }

    /**
     * 对list数据源将其里面的数据进行脱敏处理
     *
     * @param list
     * @return 结果
     */
    public AjaxResult desensitizedList(List<T> list){

        if (list == null){
            return AjaxResult.error("脱敏数据为空");
        }

        // 初始化数据
        this.init(list);

        int failTimes = 0;

        for (T t: this.list) {
            if ((Integer)desensitization(t).get("code") != HttpStatus.SUCCESS){
                failTimes++;
            }
        }

        if (failTimes >0){
            return AjaxResult.error("脱敏操作中出现失败",failTimes);
        }

        return AjaxResult.success();
    }

    /**
     * 放到字段集合中
     */
    private void putToField(Field field, Sensitive attr)
    {
        if (attr != null)
        {
            this.fields.add(new Object[] { field, attr });
        }
    }

    /**
     * 脱敏:JavaBean模式脱敏
     *
     * @param t 需要脱敏的对象
     * @return
     */
    public AjaxResult desensitization(T t) {
        if (t == null){
            return AjaxResult.error("脱敏数据为空");
        }

        // 初始化数据
        init(t);

        try {
            // 遍历处理需要进行 脱敏的字段
            for (Object[] os : fields)
            {
                Field field = (Field) os[0];
                Sensitive sensitive = (Sensitive) os[1];
                // 设置实体类私有属性可访问
                field.setAccessible(true);
                desensitizeField(sensitive,t,field);
            }
            return AjaxResult.success(t);
        } catch (Exception e) {
            e.printStackTrace();
            log.error("日志脱敏处理失败,回滚,详细信息:[{}]", e);
            return AjaxResult.error("脱敏处理失败",e);
        }
    }

    /**
     * 对类的属性进行脱敏
     *
     * @param attr 脱敏参数
     * @param vo 脱敏对象
     * @param field 脱敏属性
     * @return
     */
    private void desensitizeField(Sensitive attr, T vo, Field field) throws IllegalAccessException {

        if (attr == null || vo == null || field == null){
            return ;
        }

        // 读取对象中的属性
        Object value = field.get(vo);
        SensitiveType sensitiveType = attr.type();
        int prefixNoMaskLen = attr.prefixNoMaskLen();
        int suffixNoMaskLen = attr.suffixNoMaskLen();
        String symbol = attr.symbol();

        //获取属性后现在默认处理的是String类型,其他类型数据可扩展
        Object val = convertByType(sensitiveType, value, prefixNoMaskLen, suffixNoMaskLen, symbol);
        field.set(vo, val);
    }

    /**
     * 以类的属性的get方法方法形式获取值
     *
     * @param o 对象
     * @param name 属性名
     * @return value
     * @throws Exception
     */
    private Object getValue(Object o, String name) throws Exception
    {
        if (StringUtils.isNotNull(o) && StringUtils.isNotEmpty(name))
        {
            Class<?> clazz = o.getClass();
            Field field = clazz.getDeclaredField(name);
            field.setAccessible(true);
            o = field.get(o);
        }
        return o;
    }

    /**
     * 根据不同注解类型处理不同字段
     */
    private Object convertByType(SensitiveType sensitiveType, Object value, int prefixNoMaskLen, int suffixNoMaskLen, String symbol) {
        switch (sensitiveType) {
            case CUSTOMER:
                value = customer(value, prefixNoMaskLen, suffixNoMaskLen, symbol);
                break;
            case CHINESE_NAME:
                value = chineseName(value, symbol);
                break;
            case ID_CARD_NUM:
                value = idCardNum(value, symbol);
                break;
            case MOBILE_PHONE:
                value = mobilePhone(value, symbol);
                break;
            case FIXED_PHONE:
                value = fixedPhone(value, symbol);
                break;
            case PASSWORD:
                value = password(value, symbol);
                break;
            case BANKCARD:
                value = bankCard(value, symbol);
                break;
            case EMAIL:
                value = email(value, symbol);
                break;
            case ADDRESS:
                value = address(value, symbol);
                break;
        }
        return value;
    }

    /*--------------------------下面的脱敏工具类也可以单独对某一个字段进行使用-------------------------*/

    /**
     * 【自定义】 根据设置进行配置
     *
     * @param value 需处理数据
     * @param symbol 填充字符
     * @return 脱敏后数据
     */
    public Object customer(Object value, int prefixNoMaskLen, int suffixNoMaskLen, String symbol) {

        //针对字符串的处理
        if (value instanceof String){
            return handleString((String) value, prefixNoMaskLen, suffixNoMaskLen, symbol);
        }

        return value;
    }

    /**
     * 对字符串进行脱敏处理
     *
     * @param s 需处理数据
     * @param prefixNoMaskLen 开头展示字符长度
     * @param suffixNoMaskLen 结尾展示字符长度
     * @param symbol 填充字符
     * @return
     */
    private String handleString(String s, int prefixNoMaskLen, int suffixNoMaskLen, String symbol){
        // 是否为空
        if (StringUtils.isBlank(s)) {
            return "";
        }

        // 如果设置为空之类使用 * 代替
        if (StringUtils.isBlank(symbol)){
            symbol = "*";
        }

        // 对长度进行判断
        int length = s.length();
        if (length > prefixNoMaskLen + suffixNoMaskLen){
            String namePrefix = StringUtils.left(s, prefixNoMaskLen);
            String nameSuffix = StringUtils.right(s, suffixNoMaskLen);
            s = StringUtils.rightPad(namePrefix, StringUtils.length(s) - suffixNoMaskLen, symbol).concat(nameSuffix);
        }

        return s;
    }

    /**
     * 【中文姓名】只显示第一个汉字,其他隐藏为2个星号,比如:李**
     *
     * @param value 需处理数据
     * @param symbol 填充字符
     * @return 脱敏后数据
     */
    public String chineseName(Object value, String symbol) {

        //针对字符串的处理
        if (value instanceof String){
            // 对前后长度进行设置 默认 开头只展示一个字符
            int prefixNoMaskLen = 1;
            int suffixNoMaskLen = 0;

            return handleString((String) value, prefixNoMaskLen, suffixNoMaskLen, symbol);
        }
        return "";
    }

    /**
     * 【身份证号】显示最后四位,其他隐藏。共计18位或者15位,比如:*************1234
     *
     * @param value 需处理数据
     * @param symbol 填充字符
     * @return 脱敏后数据
     */
    public String idCardNum(Object value, String symbol) {

        //针对字符串的处理
        if (value instanceof String){
            // 对前后长度进行设置 默认 结尾只展示四个字符
            int prefixNoMaskLen = 0;
            int suffixNoMaskLen = 4;

            return handleString((String) value, prefixNoMaskLen, suffixNoMaskLen, symbol);
        }
        return "";
    }

    /**
     * 【固定电话】 显示后四位,其他隐藏,比如:*******3241
     *
     * @param value 需处理数据
     * @param symbol 填充字符
     * @return 脱敏后数据
     */
    public String fixedPhone(Object value, String symbol) {

        //针对字符串的处理
        if (value instanceof String){
            // 对前后长度进行设置 默认 结尾只展示四个字符
            int prefixNoMaskLen = 0;
            int suffixNoMaskLen = 4;

            return handleString((String) value, prefixNoMaskLen, suffixNoMaskLen, symbol);
        }
        return "";

    }

    /**
     * 【手机号码】前三位,后四位,其他隐藏,比如:135****6810
     *
     * @param value 需处理数据
     * @param symbol 填充字符
     * @return 脱敏后数据
     */
    public String mobilePhone(Object value, String symbol) {

        //针对字符串的处理
        if (value instanceof String){
            // 对前后长度进行设置 默认 开头只展示三个字符  结尾只展示四个字符
            int prefixNoMaskLen = 3;
            int suffixNoMaskLen = 4;

            return handleString((String) value, prefixNoMaskLen, suffixNoMaskLen, symbol);
        }
        return "";
    }

    /**
     * 【地址】只显示到地区,不显示详细地址,比如:湖南省长沙市岳麓区***
     *  只能处理 省市区的数据
     *
     * @param value 需处理数据
     * @param symbol 填充字符
     * @return
     */
    public String address(Object value, String symbol) {

        //针对字符串的处理
        if (value instanceof String){
            // 对前后长度进行设置 默认 开头只展示九个字符
            int prefixNoMaskLen = 9;
            int suffixNoMaskLen = 0;

            return handleString((String) value, prefixNoMaskLen, suffixNoMaskLen, symbol);
        }
        return "";
    }

    /**
     * 【电子邮箱】 邮箱前缀仅显示第一个字母,前缀其他隐藏,用星号代替,@及后面的地址显示,比如:d**@126.com
     *
     * @param value 需处理数据
     * @param symbol 填充字符
     * @return 脱敏后数据
     */
    public String email(Object value, String symbol) {

        //针对字符串的处理
        if (value instanceof String){
            // 对前后长度进行设置 默认 开头只展示一个字符  结尾只展示@及后面的地址
            int prefixNoMaskLen = 1;
            int suffixNoMaskLen = 4;

            String s = (String) value;
            if (StringUtils.isBlank(s)) {
                return "";
            }

            // 获取最后一个@
            int lastIndex = StringUtils.lastIndexOf(s, "@");
            if (lastIndex <= 1) {
                return s;
            } else {
                suffixNoMaskLen = s.length() - lastIndex;
            }

            return handleString((String) value, prefixNoMaskLen, suffixNoMaskLen, symbol);
        }
        return "";

    }

    /**
     * 【银行卡号】前六位,后四位,其他用星号隐藏每位1个星号,比如:6222600**********1234
     *
     * @param value 需处理数据
     * @param symbol 填充字符
     * @return 脱敏后数据
     */
    public String bankCard(Object value, String symbol) {

        //针对字符串的处理
        if (value instanceof String){
            // 对前后长度进行设置 默认 开头只展示六个字符  结尾只展示四个字符
            int prefixNoMaskLen = 6;
            int suffixNoMaskLen = 4;

            return handleString((String) value, prefixNoMaskLen, suffixNoMaskLen, symbol);
        }
        return "";

    }

    /**
     * 【密码】密码的全部字符都用*代替,比如:******
     *
     * @param value 需处理数据
     * @param symbol 填充字符
     * @return
     */
    public String password(Object value,String symbol) {

        //针对字符串的处理
        if (value instanceof String){
            // 对前后长度进行设置 默认 开头只展示六个字符  结尾只展示四个字符
            int prefixNoMaskLen = 0;
            int suffixNoMaskLen = 0;

            return handleString((String) value, prefixNoMaskLen, suffixNoMaskLen, symbol);
        }
        return "";
    }
}
3.2 Operasi penyahpekaan
public class User {
    private static final long serialVersionUID = 1L;

    /** 普通用户ID */
    private Long userId;

    /** 昵称 */
    @Sensitive(type = SensitiveType.CUSTOMER,prefixNoMaskLen = 2,suffixNoMaskLen = 1)
    private String nickName;

    /** 姓名 */
    @Sensitive(type = SensitiveType.CHINESE_NAME)
    private String userName;

    /** 身份证 */
    @Sensitive(type = SensitiveType.ID_CARD_NUM)
    private String identityCard;

    /** 手机号码 */
    @Sensitive(type = SensitiveType.MOBILE_PHONE)
    private String phoneNumber;
}

Atas ialah kandungan terperinci Bagaimana untuk menambah fungsi penyahpekaan dalam SpringBoot. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Kenyataan:
Artikel ini dikembalikan pada:yisu.com. Jika ada pelanggaran, sila hubungi admin@php.cn Padam