SkillAgentSearch skills...

Sensitive

🔐Sensitive log tool for java, based on java annotation. (基于注解的 java 日志脱敏工具框架,更加优雅的日志打印。支持自定义哈希、支持基于 log4j2 插件的统一脱敏、支持 logback 插件统一脱敏)

Install / Use

/learn @houbb/Sensitive

README

项目介绍

日志脱敏是常见的安全需求。普通的基于工具类方法的方式,对代码的入侵性太强,编写起来又特别麻烦。

sensitive 项目提供基于注解的方式,并且内置了常见的脱敏方式,便于开发。

支持 logback 和 log4j2 等常见的日志脱敏插件。

日志插件解决正则匹配长文本可能出现的回溯问题,性能远超正则

Build Status Maven Central Open Source Love

日志脱敏

为了金融交易的安全性,国家强制规定对于以下信息是要日志脱敏的:

  1. 用户名

  2. 手机号

  3. 邮箱

  4. 银行卡号

  5. 密码

  6. 身份证号

持久化加密

存储的时候上面的信息都需要加密,密码为不可逆加密,其他为可逆加密。

类似的功能有很多。不在本系统的解决范围内。

特性

  1. 基于注解的日志脱敏。

  2. 可以自定义策略实现,策略生效条件。

  3. 内置常见的十几种脱敏内置方案。

  4. java 深拷贝,且原始对象不用实现任何接口。

5. 支持用户自定义注解。

6. 支持基于 FastJSON 直接生成脱敏后的 json

7. 支持自定义哈希策略,更加方便定位日志问题

8. 支持基于 log4j2 的统一脱敏策略

9. 支持基于 logback 的统一脱敏策略

项目推荐

下面是一些日志、加解密、脱敏安全相关的库推荐:

| 项目 | 介绍 | |:----------------------------------------------------------------------|:----------------------| | sensitive-word | 高性能敏感词核心库 | | sensitive-word-admin | 敏感词控台,前后端分离 | | sensitive | 高性能日志脱敏组件 | | auto-log | 统一日志切面组件,支持全链路traceId | | encryption-local | 离线加密机组件 | | encryption | 加密机标准API+本地客户端 | | encryption-server | 加密机服务 |

变更日志

变更日志

v-1.6.0 新特性

  • 添加 logback 脱敏插件

拓展阅读

日志开源组件(一)java 注解结合 spring aop 实现自动输出日志

日志开源组件(二)java 注解结合 spring aop 实现日志traceId唯一标识

日志开源组件(三)java 注解结合 spring aop 自动输出日志新增拦截器与过滤器

日志开源组件(四)如何动态修改 spring aop 切面信息?让自动日志输出框架更好用

日志开源组件(五)如何将 dubbo filter 拦截器原理运用到日志拦截器中?

日志开源组件(六)Adaptive Sampling 自适应采样

高性能日志脱敏组件(一)java 日志脱敏框架 sensitive,优雅的打印脱敏日志

高性能日志脱敏组件(二)金融用户敏感数据如何优雅地实现脱敏?

高性能日志脱敏组件(三)日志脱敏之后,无法根据信息快速定位怎么办?

高性能日志脱敏组件(四)基于 log4j2 插件实现统一日志脱敏,性能远超正则替换

高性能日志脱敏组件(五)已支持 log4j2 和 logback 插件

快速开始

环境准备

JDK 1.8+

Maven 3.x

maven 导入

<dependency>
    <groupId>com.github.houbb</groupId>
    <artifactId>sensitive-core</artifactId>
    <version>1.7.0</version>
</dependency>

核心 api 简介

SensitiveUtil 工具类的核心方法列表如下:

| 序号 | 方法 | 参数 | 结果 | 说明 | |:---|:---|:---|:---|:---| | 1 | desCopy() | 目标对象 | 深度拷贝脱敏对象 | 适应性更强 | | 2 | desJson() | 目标对象 | 脱敏对象 json | 性能较好 | | 3 | desCopyCollection() | 目标对象集合 | 深度拷贝脱敏对象集合 | | | 4 | desJsonCollection() | 目标对象集合 | 脱敏对象 json 集合 | |

定义对象

  • UserAnnotationBean.java

通过注解,指定每一个字段的脱敏策略。

public class UserAnnotationBean {

    @SensitiveStrategyChineseName
    private String username;

    @SensitiveStrategyPassword
    private String password;

    @SensitiveStrategyPassport
    private String passport;

    @SensitiveStrategyIdNo
    private String idNo;

    @SensitiveStrategyCardId
    private String bandCardId;

    @SensitiveStrategyPhone
    private String phone;

    @SensitiveStrategyEmail
    private String email;

    @SensitiveStrategyAddress
    private String address;

    @SensitiveStrategyBirthday
    private String birthday;

    @SensitiveStrategyGps
    private String gps;

    @SensitiveStrategyIp
    private String ip;

    @SensitiveStrategyMaskAll
    private String maskAll;

    @SensitiveStrategyMaskHalf
    private String maskHalf;

    @SensitiveStrategyMaskRange
    private String maskRange;

    //Getter & Setter
    //toString()
}
  • 数据准备

构建一个最简单的测试对象:

UserAnnotationBean bean  = new UserAnnotationBean();
bean.setUsername("张三");
bean.setPassword("123456");
bean.setPassport("CN1234567");
bean.setPhone("13066668888");
bean.setAddress("中国上海市浦东新区外滩18号");
bean.setEmail("whatanice@code.com");
bean.setBirthday("20220831");
bean.setGps("66.888888");
bean.setIp("127.0.0.1");
bean.setMaskAll("可恶啊我会被全部掩盖");
bean.setMaskHalf("还好我只会被掩盖一半");
bean.setMaskRange("我比较灵活指定掩盖范围");
bean.setBandCardId("666123456789066");
bean.setIdNo("360123202306018888");
  • 测试代码
final String originalStr = "UserAnnotationBean{username='张三', password='123456', passport='CN1234567', idNo='360123202306018888', bandCardId='666123456789066', phone='13066668888', email='whatanice@code.com', address='中国上海市浦东新区外滩18号', birthday='20220831', gps='66.888888', ip='127.0.0.1', maskAll='可恶啊我会被全部掩盖', maskHalf='还好我只会被掩盖一半', maskRange='我比较灵活指定掩盖范围'}";
final String sensitiveStr = "UserAnnotationBean{username='张*', password='null', passport='CN*****67', idNo='3****************8', bandCardId='666123*******66', phone='1306****888', email='wh************.com', address='中国上海********8号', birthday='20*****1', gps='66*****88', ip='127***0.1', maskAll='**********', maskHalf='还好我只会*****', maskRange='我*********围'}";
final String expectSensitiveJson = "{\"address\":\"中国上海********8号\",\"bandCardId\":\"666123*******66\",\"birthday\":\"20*****1\",\"email\":\"wh************.com\",\"gps\":\"66*****88\",\"idNo\":\"3****************8\",\"ip\":\"127***0.1\",\"maskAll\":\"**********\",\"maskHalf\":\"还好我只会*****\",\"maskRange\":\"我*********围\",\"passport\":\"CN*****67\",\"phone\":\"1306****888\",\"username\":\"张*\"}";

UserAnnotationBean sensitiveUser = SensitiveUtil.desCopy(bean);
Assert.assertEquals(sensitiveStr, sensitiveUser.toString());
Assert.assertEquals(originalStr, bean.toString());

String sensitiveJson = SensitiveUtil.desJson(bean);
Assert.assertEquals(expectSensitiveJson, sensitiveJson);

我们可以直接利用 sensitiveUser 去打印日志信息,而这个对象对于代码其他流程不影响,我们依然可以使用原来的 user 对象。

当然,也可以使用 sensitiveJson 打印日志信息。

@Sensitive 注解

说明

@SensitiveStrategyChineseName 这种注解是为了便于用户使用,本质上等价于 @Sensitive(strategy = StrategyChineseName.class)

@Sensitive 注解可以指定对应的脱敏策略。

内置注解与映射

| 编号 | 注解 | 等价 @Sensitive | 备注 | |:---|:--------------------------------|:---------------------------------------------------|:---------| | 1 | @SensitiveStrategyChineseName | @Sensitive(strategy = StrategyChineseName.class) | 中文名称脱敏 | | 2 | @SensitiveStrategyPassword | @Sensitive(strategy = StrategyPassword.class) | 密码脱敏 | | 3 | @SensitiveStrategyEmail | @Sensitive(strategy = StrategyEmail.class) | email 脱敏 | | 4 | @SensitiveStrategyCardId | @Sensitive(strategy = StrategyCardId.class) | 卡号脱敏 | | 5 | @SensitiveStrategyPhone | @Sensitive(strategy = StrategyPhone.class) | 手机号脱敏 | | 6 | @SensitiveStrategyIdNo | @Sensitive(strategy = StrategyIdNo.class) | 身份证脱敏 | | 6 | @SensitiveStrategyAddress | @Sensitive(strategy = StrategyAddress.class) | 地址脱敏 | | 7 | @SensitiveStrategyGps | @Sensitive(strategy = StrategyGps.class) | GPS 脱敏 | | 8 | @SensitiveStrategyIp | @Sensitive(strategy = StrategyIp.class) | IP 脱敏 | | 9 | @SensitiveStrategyBirthday | @Sensitive(strategy = StrategyBirthday.class) | 生日脱敏 | | 10 | @SensitiveStrategyPassport | @Sensitive(strategy = StrategyPassport.class) | 护照脱敏 | | 11 | @SensitiveStrategyMaskAll | @Sensitive(strategy = StrategyMaskAll.class) | 全部脱敏 | | 12 | @SensitiveStrategyMaskHalf | @Sensitive(strategy = StrategyMaskHalf.class) | 一半脱敏 | | 13 | @SensitiveStrategyMaskRange | @Sensitive(strategy = StrategyMaskRange.class) | 指定范围脱敏 |

@Sensitive 定义

@Inherited
@Documented
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Sensitive {

    /**
     * 注解生效的条件
     * @return 条件对应的实现类
     */
    Class<? extends ICondition> condition() default ConditionAlwaysTrue.class;

    /**
     * 执行的策略
     * @return 策略对应的类型
     */
    Class<? extends IStrategy> strategy();

}

与 @Sensitive 混合使用

如果你将新增的注解 @SensitiveStrategyChineseName@Sensitive 同时在一个字段上使用。

为了简化逻辑,优先选择执行 @Sensitive,如果 @Sensitive 执行脱敏, 那么 @SensitiveStrategyChineseName 将不会生效。

如:

/**
 * 测试字段
 * 1.当多种注解混合的时候,为了简化逻辑,优先选择 @Sensitive 注解。
 */
@SensitiveStrategyChineseName
@Sensitive(strategy = StrategyPassword.class)
private String testField;

更多特性

自定义脱敏策略生效的场景

默认情况下,我们指定的场景都是生效的。

但是你可能需要有些情况下不进行脱敏,比如有些用户密码为 123456,你觉得这种用户不脱敏也罢。

  • UserPasswordCondition.java
@Sensitive(condition = ConditionFooPassword.class, strategy = StrategyPassword.class)
private String pa
View on GitHub
GitHub Stars702
CategoryDevelopment
Updated10d ago
Forks190

Languages

Java

Security Score

85/100

Audited on Mar 17, 2026

No findings