码农知识堂 - 1000bd
  •   Python
  •   PHP
  •   JS/TS
  •   JAVA
  •   C/C++
  •   C#
  •   GO
  •   Kotlin
  •   Swift
  • SpringBoot用一个自定义注解来轻松搞定接口返回的数据脱敏


    在实际的项目当中经常会有产品要求某个返回的字段需要数据脱敏,比如手机号和其它敏感字段,本文就讲解如何使用一个自定义注解来进行数据的脱敏

    自定义脱敏注解

    /**
     * @author WuSong
     * @version 1.0
     * @date 2022/8/29 11:44
     * @description
     */
    @Target({ElementType.FIELD, ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface DataMasking {
    
        DataMaskingFunc maskFunc() default DataMaskingFunc.NO_MASK;
    
    }
    

    接口:

    /**
     * @author WuSong
     * @version 1.0
     * @date 2022/8/29 11:44
     * @description
     */
    public interface DataMaskingOperation {
    
        char MASK_CHAR = '*';
    
        String mask(String content, Character maskChar);
    
    }
    

    枚举:

    /**
     * @author WuSong
     * @version 1.0
     * @date 2022/8/29 11:44
     * @description
     */
    public enum DataMaskingFunc {
        /**
         *  脱敏转换器
         */
        NO_MASK((str, maskChar) -> {
            return str;
        }),
        PART_MASK((str, maskChar) -> {
            if (StringUtils.hasLength(str) && str.length()>=5) {
                StringBuilder sb = new StringBuilder();
                sb.append(str);
                for (int i = 2; i < str.length()-2; i++) {
                    sb.setCharAt(i,StringUtils.hasLength(String.valueOf(maskChar)) ? maskChar : DataMaskingOperation.MASK_CHAR);
                }
                return sb.toString();
            } else {
                return str;
            }
        }),
        ALL_MASK((str, maskChar) -> {
            if (StringUtils.hasLength(str)) {
                StringBuilder sb = new StringBuilder();
                for (int i = 0; i < str.length(); i++) {
                    sb.append(StringUtils.hasLength(String.valueOf(maskChar)) ? maskChar : DataMaskingOperation.MASK_CHAR);
                }
                return sb.toString();
            } else {
                return str;
            }
        });
    
        private final DataMaskingOperation operation;
    
        private DataMaskingFunc(DataMaskingOperation operation) {
            this.operation = operation;
        }
    
        public DataMaskingOperation operation() {
            return this.operation;
        }
    }

    类:

    /**
     * @author WuSong
     * @version 1.0
     * @date 2022/8/29 11:47
     * @description
     */
    @Configuration(
            proxyBeanMethods = false
    )
    public class DataMaskConfiguration {
    
        @Configuration(
                proxyBeanMethods = false
        )
        @ConditionalOnClass({Jackson2ObjectMapperBuilder.class})
        static class JacksonObjectMapperConfiguration {
            JacksonObjectMapperConfiguration() {
            }
    
            @Bean
            @Primary
            ObjectMapper jacksonObjectMapper(Jackson2ObjectMapperBuilder builder) {
                ObjectMapper objectMapper = builder.createXmlMapper(false).build();
                AnnotationIntrospector ai = objectMapper.getSerializationConfig().getAnnotationIntrospector();
                AnnotationIntrospector newAi = AnnotationIntrospectorPair.pair(ai, new DataMaskingAnnotationIntrospector());
                objectMapper.setAnnotationIntrospector(newAi);
                return objectMapper;
            }
        }
    
    }
    
    /**
     * @author WuSong
     * @version 1.0
     * @date 2022/8/29 11:46
     * @description
     */
    @Slf4j
    public class DataMaskingAnnotationIntrospector extends NopAnnotationIntrospector {
    
        @Override
        public Object findSerializer(Annotated am) {
            DataMasking annotation = am.getAnnotation(DataMasking.class);
            if (annotation != null) {
                return new DataMaskingSerializer(annotation.maskFunc().operation());
            }
            return null;
        }
    
    }
    

    /**
     * @author WuSong
     * @version 1.0
     * @date 2022/8/29 11:45
     * @description
     */
    public final class DataMaskingSerializer extends StdScalarSerializer {
        private final DataMaskingOperation operation;
    
        public DataMaskingSerializer() {
            super(String.class, false);
            this.operation = null;
        }
    
        public DataMaskingSerializer(DataMaskingOperation operation) {
            super(String.class, false);
            this.operation = operation;
        }
    
        @Override
        public boolean isEmpty(SerializerProvider prov, Object value) {
            String str = (String)value;
            return str.isEmpty();
        }
    
        @Override
        public void serialize(Object value, JsonGenerator gen, SerializerProvider provider) throws IOException {
            if (Objects.isNull(operation)) {
                String content = DataMaskingFunc.PART_MASK.operation().mask((String) value, '*');
                gen.writeString(content);
            } else {
                String content = operation.mask((String) value, '*');
                gen.writeString(content);
            }
        }
    
        @Override
        public final void serializeWithType(Object value, JsonGenerator gen, SerializerProvider provider, TypeSerializer typeSer) throws IOException {
            this.serialize(value, gen, provider);
        }
    
        @Override
        public JsonNode getSchema(SerializerProvider provider, Type typeHint) {
            return this.createSchemaNode("string", true);
        }
    
        @Override
        public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) throws JsonMappingException {
            this.visitStringFormat(visitor, typeHint);
        }
    }
     
     
    

    具体使用:

    /**
     * @Author WuSong
     * @Date 2022-08-02 11:39
     */
    @Data
    public class FindCouponCertificatesPageResponse {
    
           /**
             * 手机号码
             */
            @ApiModelProperty("手机号码")
            @DataMasking(maskFunc = DataMaskingFunc.PART_MASK)
            private String phone;
    }
    

    返回截图:

    总结:

    使用自定义注解的方式来进行数据的脱敏可以减少在代码中的代码行数,更具可读性,符合开闭原则。 

  • 相关阅读:
    MYSQL8.0 WITH RECURSIVE递归查询
    最大路径和
    二十三、Redis集群
    09、SpringCloud之Gateway网关组件学习笔记
    C#实现HTTP访问类HttpHelper
    【机组】计算机系统组成课程笔记 第二章 计算机中的信息表示
    分析:通过哪种方法来建立股票量化交易数据库?
    掌握pandas cut函数,一键实现数据分类
    网络基础(一)
    公众号查题接口 对接教程说明
  • 原文地址:https://blog.csdn.net/qq_41625866/article/details/126583202
    • 最新文章
    • 攻防演习之三天拿下官网站群
      数据安全治理学习——前期安全规划和安全管理体系建设
      企业安全 | 企业内一次钓鱼演练准备过程
      内网渗透测试 | Kerberos协议及其部分攻击手法
      0day的产生 | 不懂代码的"代码审计"
      安装scrcpy-client模块av模块异常,环境问题解决方案
      leetcode hot100【LeetCode 279. 完全平方数】java实现
      OpenWrt下安装Mosquitto
      AnatoMask论文汇总
      【AI日记】24.11.01 LangChain、openai api和github copilot
    • 热门文章
    • 十款代码表白小特效 一个比一个浪漫 赶紧收藏起来吧!!!
      奉劝各位学弟学妹们,该打造你的技术影响力了!
      五年了,我在 CSDN 的两个一百万。
      Java俄罗斯方块,老程序员花了一个周末,连接中学年代!
      面试官都震惊,你这网络基础可以啊!
      你真的会用百度吗?我不信 — 那些不为人知的搜索引擎语法
      心情不好的时候,用 Python 画棵樱花树送给自己吧
      通宵一晚做出来的一款类似CS的第一人称射击游戏Demo!原来做游戏也不是很难,连憨憨学妹都学会了!
      13 万字 C 语言从入门到精通保姆级教程2021 年版
      10行代码集2000张美女图,Python爬虫120例,再上征途
    Copyright © 2022 侵权请联系2656653265@qq.com    京ICP备2022015340号-1
    正则表达式工具 cron表达式工具 密码生成工具

    京公网安备 11010502049817号