金融用户敏感数据如何优雅地实现脱敏?

这篇具有很好参考价值的文章主要介绍了金融用户敏感数据如何优雅地实现脱敏?。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

项目介绍

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

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

日志脱敏

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

  1. 用户名

  2. 手机号

  3. 邮箱

  4. 银行卡号

  5. 密码

  6. 身份证号

持久化加密

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

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

特性

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

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

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

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

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

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

变更日志

变更日志

快速开始

环境准备

JDK 7+

Maven 3.x

maven 导入

<dependency>
    <groupId>com.github.houbb</groupId>
    <artifactId>sensitive-core</artifactId>
    <version>1.0.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 password;

其他保持不变,我们指定了一个 condition,实现如下:

  • ConditionFooPassword.java
public class ConditionFooPassword implements ICondition {
    @Override
    public boolean valid(IContext context) {
        try {
            Field field = context.getCurrentField();
            final Object currentObj = context.getCurrentObject();
            final String password = (String) field.get(currentObj);
            return !password.equals("123456");
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        }
    }
}

也就是只有当密码不是 123456 时密码脱敏策略才会生效。

属性为集合或者对象

如果某个属性是单个集合或者对象,则需要使用注解 @SensitiveEntry

  • 放在集合属性上,且属性为普通对象

会遍历每一个属性,执行上面的脱敏策略。

  • 放在对象属性上

会处理对象中各个字段上的脱敏注解信息。

  • 放在集合属性上,且属性为对象

遍历每一个对象,处理对象中各个字段上的脱敏注解信息。

放在集合属性上,且属性为普通对象

  • UserEntryBaseType.java

作为演示,集合中为普通的字符串。

public class UserEntryBaseType {

    @SensitiveEntry
    @Sensitive(strategy = StrategyChineseName.class)
    private List<String> chineseNameList;

    @SensitiveEntry
    @Sensitive(strategy = StrategyChineseName.class)
    private String[] chineseNameArray;
    
    //Getter & Setter & toString()
}

放在对象属性上

例子如下:

public class UserEntryObject {

    @SensitiveEntry
    private User user;

    @SensitiveEntry
    private List<User> userList;

    @SensitiveEntry
    private User[] userArray;
    
    //...
}

自定义注解

  • v0.0.4 新增功能。允许功能自定义条件注解和策略注解。
  • v0.0.11 新增功能。允许功能自定义级联脱敏注解。

案例1

自定义密码脱敏策略&自定义密码脱敏策略生效条件

  • 策略脱敏
/**
 * 自定义密码脱敏策略
 * @author binbin.hou
 * date 2019/1/17
 * @since 0.0.4
 */
@Inherited
@Documented
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@SensitiveStrategy(CustomPasswordStrategy.class)
public @interface SensitiveCustomPasswordStrategy {
}
  • 脱敏生效条件
/**
 * 自定义密码脱敏策略生效条件
 * @author binbin.hou
 * date 2019/1/17
 * @since 0.0.4
 */
@Inherited
@Documented
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@SensitiveCondition(ConditionFooPassword.class)
public @interface SensitiveCustomPasswordCondition{
}
  • TIPS

@SensitiveStrategy 策略单独使用的时候,默认是生效的。

如果有 @SensitiveCondition 注解,则只有当条件满足时,才会执行脱敏策略。

@SensitiveCondition 只会对系统内置注解和自定义注解生效,因为 @Sensitive 有属于自己的策略生效条件。

  • 策略优先级

@Sensitive 优先生效,然后是系统内置注解,最后是用户自定义注解。

对应的实现

两个元注解 @SensitiveStrategy@SensitiveCondition 分别指定了对应的实现。

  • CustomPasswordStrategy.java
public class CustomPasswordStrategy implements IStrategy {

    @Override
    public Object des(Object original, IContext context) {
        return "**********************";
    }

}
  • ConditionFooPassword.java
/**
 * 让这些 123456 的密码不进行脱敏
 * @author binbin.hou
 * date 2019/1/2
 * @since 0.0.1
 */
public class ConditionFooPassword implements ICondition {
    @Override
    public boolean valid(IContext context) {
        try {
            Field field = context.getCurrentField();
            final Object currentObj = context.getCurrentObject();
            final String name = (String) field.get(currentObj);
            return !name.equals("123456");
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        }
    }

}

定义测试对象

定义一个使用自定义注解的对象。

public class CustomPasswordModel {

    @SensitiveCustomPasswordCondition
    @SensitiveCustomPasswordStrategy
    private String password;

    @SensitiveCustomPasswordCondition
    @SensitiveStrategyPassword
    private String fooPassword;
    
    //其他方法
}

测试

/**
 * 自定义注解测试
 */
@Test
public void customAnnotationTest() {
    final String originalStr = "CustomPasswordModel{password='hello', fooPassword='123456'}";
    final String sensitiveStr = "CustomPasswordModel{password='**********************', fooPassword='123456'}";
    CustomPasswordModel model = buildCustomPasswordModel();
    Assert.assertEquals(originalStr, model.toString());

    CustomPasswordModel sensitive = SensitiveUtil.desCopy(model);
    Assert.assertEquals(sensitiveStr, sensitive.toString());
    Assert.assertEquals(originalStr, model.toString());
}

构建对象的方法如下:

/**
 * 构建自定义密码对象
 * @return 对象
 */
private CustomPasswordModel buildCustomPasswordModel(){
    CustomPasswordModel model = new CustomPasswordModel();
    model.setPassword("hello");
    model.setFooPassword("123456");
    return model;
}

案例2

  • v0.0.11 新增功能。允许功能自定义级联脱敏注解。

自定义级联脱敏注解

  • 自定义级联脱敏注解

可以根据自己的业务需要,在自定义的注解上使用 @SensitiveEntry

使用方式保持和 @SensitiveEntry 一样即可。

/**
 * 级联脱敏注解,如果对象中属性为另外一个对象(集合),则可以使用这个注解指定。
 * <p>
 * 1. 如果属性为 Iterable 的子类集合,则当做列表处理,遍历其中的对象
 * 2. 如果是普通对象,则处理对象中的脱敏信息
 * 3. 如果是普通字段/MAP,则不做处理
 * @since 0.0.11
 */
@Inherited
@Documented
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@SensitiveEntry
public @interface SensitiveEntryCustom {
}

定义测试对象

定义一个使用自定义注解的对象。

public class CustomUserEntryObject {

    @SensitiveEntryCustom
    private User user;

    @SensitiveEntryCustom
    private List<User> userList;

    @SensitiveEntryCustom
    private User[] userArray;

    // 其他方法...
}

生成脱敏后的 JSON

说明

为了避免生成中间脱敏对象,v0.0.6 之后直接支持生成脱敏后的 JSON。

使用方法

新增工具类方法,可以直接返回脱敏后的 JSON。

生成的 JSON 是脱敏的,原对象属性值不受影响。

public static String desJson(Object object)

注解的使用方式

SensitiveUtil.desCopy() 完全一致。

使用示例代码

所有的测试案例中,都添加了对应的 desJson(Object) 测试代码,可以参考。

此处只展示最基本的使用。

final String originalStr = "SystemBuiltInAt{phone='18888888888', password='1234567', name='脱敏君', email='12345@qq.com', cardId='123456190001011234'}";
final String sensitiveJson = "{\"cardId\":\"123456**********34\",\"email\":\"12******.com\",\"name\":\"脱**\",\"phone\":\"1888****888\"}";

SystemBuiltInAt systemBuiltInAt = DataPrepareTest.buildSystemBuiltInAt();
Assert.assertEquals(sensitiveJson, SensitiveUtil.desJson(systemBuiltInAt));
Assert.assertEquals(originalStr, systemBuiltInAt.toString());

注意

本次 JSON 脱敏基于 FastJSON。

FastJSON 在序列化本身存在一定限制。当对象中有集合,集合中还是对象时,结果不尽如人意。

示例代码

本测试案例可见测试代码。

final String originalStr = "UserCollection{userList=[User{username='脱敏君', idCard='123456190001011234', password='1234567', email='12345@qq.com', phone='18888888888'}], userSet=[User{username='脱敏君', idCard='123456190001011234', password='1234567', email='12345@qq.com', phone='18888888888'}], userCollection=[User{username='脱敏君', idCard='123456190001011234', password='1234567', email='12345@qq.com', phone='18888888888'}], userMap={map=User{username='脱敏君', idCard='123456190001011234', password='1234567', email='12345@qq.com', phone='18888888888'}}}";
final String commonJson = "{\"userArray\":[{\"email\":\"12345@qq.com\",\"idCard\":\"123456190001011234\",\"password\":\"1234567\",\"phone\":\"18888888888\",\"username\":\"脱敏君\"}],\"userCollection\":[{\"$ref\":\"$.userArray[0]\"}],\"userList\":[{\"$ref\":\"$.userArray[0]\"}],\"userMap\":{\"map\":{\"$ref\":\"$.userArray[0]\"}},\"userSet\":[{\"$ref\":\"$.userArray[0]\"}]}";
final String sensitiveJson = "{\"userArray\":[{\"email\":\"12******.com\",\"idCard\":\"123456**********34\",\"phone\":\"1888****888\",\"username\":\"脱**\"}],\"userCollection\":[{\"$ref\":\"$.userArray[0]\"}],\"userList\":[{\"$ref\":\"$.userArray[0]\"}],\"userMap\":{\"map\":{\"$ref\":\"$.userArray[0]\"}},\"userSet\":[{\"$ref\":\"$.userArray[0]\"}]}";

UserCollection userCollection = DataPrepareTest.buildUserCollection();

Assert.assertEquals(commonJson, JSON.toJSONString(userCollection));
Assert.assertEquals(sensitiveJson, SensitiveUtil.desJson(userCollection));
Assert.assertEquals(originalStr, userCollection.toString());

解决方案

如果有这种需求,建议使用原来的 desCopy(Object)

脱敏引导类

为了配置的灵活性,引入了引导类。

核心 api 简介

SensitiveBs 引导类的核心方法列表如下:

序号 方法 参数 结果 说明
1 desCopy() 目标对象 深度拷贝脱敏对象 适应性更强
2 desJson() 目标对象 脱敏对象 json 性能较好

使用示例

使用方式和工具类一致,示意如下:

SensitiveBs.newInstance().desCopy(user);

配置深度拷贝实现

默认的使用 FastJson 进行对象的深度拷贝,等价于:

SensitiveBs.newInstance()
                .deepCopy(FastJsonDeepCopy.getInstance())
                .desJson(user);

参见 SensitiveBsTest.java

deepCopy 用于指定深度复制的具体实现,支持用户自定义。

深度复制(DeepCopy)

说明

深度复制可以保证我们日志输出对象脱敏,同时不影响正常业务代码的使用。

可以实现深度复制的方式有很多种,默认基于 fastjson 实现的。

为保证后续良性发展,v0.0.13 版本之后将深度复制接口抽离为单独的项目:

deep-copy

内置策略

目前支持 6 种基于序列化实现的深度复制,便于用户替换使用。

每一种都可以单独使用,保证依赖更加轻量。

自定义

为满足不同场景的需求,深度复制策略支持用户自定义。

自定义深度复制

开源地址

https://github.com/houbb/sensitive 文章来源地址https://www.toymoban.com/news/detail-466103.html

到了这里,关于金融用户敏感数据如何优雅地实现脱敏?的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 数据守护盾牌:敏感数据扫描与脱敏,让安全合规无忧

    在信息时代,数据已经成为企业和组织的核心资产,其价值与日俱增。然而,随着数据使用的普及和复杂度的提升,数据安全与合规问题也变得越来越突出。敏感数据的保护显得尤为重要,因为这些数据一旦泄露或被不当使用,可能会给组织带来巨大的风险。为了应对这一挑

    2024年01月17日
    浏览(39)
  • 数据守护盾牌:敏感数据扫描与脱敏,让安全合规无忧,程序员经验分享

    我们还提供了官网内置的规则库(如上图),如信用卡号、个人信息、密钥等。您可以选择所需扫描的敏感数据类型,并支持多选后一键创建定义匹配规则;进入到创建页面后,您还可以进一步灵活编辑规则。 敏感数据扫描规则创建完成后,您还可以在列表处实现统一查询和

    2024年04月25日
    浏览(37)
  • 对敏感信息脱敏,如对姓名、证件号码、手机号码、银行卡号码进行脱敏

    1、脱敏规则一: 显示姓名中的第一个字, 其它用*号代替。 显示姓名中的第一个字,如为英文等其他语种,也是显示第一个字母。 其它用*号代替。 ① 举例  张*        王**        A**** ② 实现方法 ③ demo 2、脱敏规则二: 显示姓名中的第一个和最后一个字, 其它用

    2024年02月06日
    浏览(50)
  • 日志里的敏感信息还在打明文?3 种日志脱敏方案任你选

    关注公众号【 1024个为什么 】,及时接收最新推送文章! 下面链接文章中重新做了梳理,补充了基于 log4j  的解决方案,建议大家阅读最新文章。 《一次性解决打日志时的4个重复低效场景(日志脱敏、日期格式化、json序列化)》 我们打的日志中经常包含姓名、手机号、银行

    2023年04月08日
    浏览(71)
  • Java如何进行数据脱敏

    MYSQL(电话号码,身份证)数据脱敏的实现 1 2 3 4 5 6 7 8 -- CONCAT()、LEFT()和RIGHT()字符串函数组合使用,请看下面具体实现 -- CONCAT(str1,str2,…):返回结果为连接参数产生的字符串 -- LEFT(str,len):返回从字符串str 开始的len 最左字符 -- RIGHT(str,len):从字符串str 开始,返回最右len 字符

    2024年02月07日
    浏览(45)
  • Python数据脱敏:如何保护您的数据

    在当今数字化时代,数据已成为企业和个人最重要的资产之一。然而,随着大量个人信息被收集和存储,保护这些信息已成为一个重要的问题。因此,对于那些需要处理大量敏感信息的人来说,数据脱敏已经成为一种必要的手段。 Python是一种强大而灵活的编程语言,在数据处

    2024年02月07日
    浏览(31)
  • 金融客户敏感信息的“精细化管控”新范式

    目    录   01  客户信息保护三箭齐发,金融IT亟需把握四个原则‍ 02  制度制约阻碍信息保护的精细化管控 ‍‍‍‍‍‍‍ 03   敏感信息精细化管控范式的6个关键设计  04   分阶段实施,形成敏感信息管控的长效运营的机制 05   未来,新挑战与新机遇并存 01‍ 加强客户信

    2024年02月11日
    浏览(46)
  • SpringBoot进阶-SpringBoot如何实现配置文件脱敏

    SpringBoot进阶-SpringBoot如何实现配置文件脱敏? SpringBoot集成jasypt配置信息加密以及采坑 在很多开发场景中我们的SpringBoot应用是被打包成了一个Jar文件来使用的,利用解压缩工具可以将这个Jar包解压出来并且在对应的配置路径下找到数据库的访问地址以及数据库的登录密码等等

    2024年02月08日
    浏览(40)
  • 一种配置化的数据脱敏与反脱敏框架实现

    在业务量日益剧增的背景下,大量数据在各种业务活动中产生,数据安全控制一直是治理的重要环节,数据脱敏属于安全控制的范畴。对互联网公司来说,数据安全一直是极为重视和敏感的话题。数据脱敏是指对某些敏感信息通过脱敏规则进行数据的变形,实现敏感隐私数据

    2024年02月16日
    浏览(38)
  • Java实现数据脱敏的方法

    在Java中,可以使用各种技术来实现数据脱敏,下面将介绍几种常见的Java实现数据脱敏的方法。 字符串截取 字符串截取是一种简单的数据脱敏方法,它将敏感数据的一部分字符替换成“”号或其他字符。例如,将身份证号码的前6位和后4位替换成“”号,这样可以保护身份证

    2024年02月16日
    浏览(45)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包