事件发布与监听publishEvent@EventListener之重要业务数据完整性加密的实现


前言

在实际的项目开发中,对了重要的数据表以及业务数据通常需要进行数据脱敏,数据记录完整性校验,部分重要业务字段的完整性校验,完整性校验先采用SM3获得加密摘要信息,再用mac算法加密摘要,最后将完整性加密结果记录到表字段今天就记录一下数据的完整性加密。

一、针对整条记录进行完整性加密

1.创建事件实体类

新建针对发布参数的不同事件实体类,实体类需要继承ApplicationEvent ,并构建一个有参构造方法。
针对传递参数为实体类的情景创建实体类UserRoleEvent4Entity :

@Data
public class UserRoleEvent4Entity extends ApplicationEvent {
    private UserRole userRole;

    public UserRoleEvent4Entity(Object source, UserRole userRole) {
        super(source);
        this.userRole = userRole;
    }
}

针对传递参数为单个param参数的情景创建实体类UserRoleEvent4Id :

@Data
public class UserRoleEvent4Id extends ApplicationEvent {
    private String id;
    public UserRoleEvent4Id(Object source, String id) {
        super(source);
        this.id = id;
    }
}

2.事件发布

serverImpl层业务逻辑中插入发布事件的代码,用到applicationContext的publishEvent方法。
针对发布内容为整个库表实体对象类型的部分代码如下:

@Override
@Transactional
 public boolean addUserRoles(String uid, String orgId, List<String> roleIds, List<String> fileList) {
 roleIds.forEach(roleId -> {
            UserRole userRole = new UserRole();
            userRole.setOrgId(orgId);
            userRole.setUid(uid);
            userRole.setRoleId(roleId);
            userRole.setCreateUid(currentUid);
            userRole.setUpdateUid(currentUid);
            userRole.setStatus(1);
            userRole.setFileIds(fileIds);
            addRoles.add(userRole);
            //发布事件
            log.info("开始发布事件");
            applicationContext.publishEvent(new UserRoleEvent4Entity(this,userRole));
        });
        saveBatch(addRoles);

针对发布内容为param参数类型的部分代码如下:

@Override
@Transactional
public boolean deleteUserRole(String uid, String orgId, String roleId) {
        //update之前拿到主键id
        UserRole userRole = this.query().eq(UserRole::getUid, uid).eq(UserRole::getOrgId, orgId)
                .eq(UserRole::getRoleId, roleId).eq(UserRole::getStatus, CommonStatusEnum.NORMAL).getOne();
        boolean result = false;
        result = this.update()
                .eq(UserRole::getUid, uid)
                .eq(UserRole::getOrgId, orgId)
                .eq(UserRole::getRoleId, roleId)
                .eq(UserRole::getStatus, CommonStatusEnum.NORMAL)
                .set(UserRole::getStatus, CommonStatusEnum.DISABLE)
                .set(UserRole::getUpdateUid, ApiUtils.currentUid())
                .set(UserRole::getUpdateTime, LocalDateTime.now())
                .execute();

        //发布事件
        if (ObjectUtils.isNotEmpty(userRole)){
            log.info("开始发布事件");
            applicationContext.publishEvent(new UserRoleEvent4Id(this,userRole.getId()));
        }
        return result;
    }

3.事件监听处理

controller层监听事件内容,获取传递的参数,对相关信息进行完整性加密。

@EventListener
@Async //异步处理防止阻塞流程
public void acceptUserRole(UserRoleEvent4Entity userRoleEvent4Entity){
        log.info("开始监听事件,并处理事件结果:"+LocalDateTime.now());
        try {
            UserRole userRole = userRoleEvent4Entity.getUserRole();
            if (ObjectUtils.isNotEmpty(userRole)){
                //先SM3再进行mac加密
                String SM3Digest = csspUtil.getSM3Digest(JSONUtil.toJsonStr(userRole));
                userRole.setMacDigest(csspUtil.getMACDigest(SM3Digest));
                userRoleService.updateById(userRole);
            }
            log.info("结束事件监听:"+LocalDateTime.now());
        }catch (Exception e){
            log.error("事件处理异常:"+e.toString());
            throw new ApiException(new ErrorCode(false,"事件处理出现异常!"));
        }

    }

方法上加上@EventListener注解监听业务代码中发布的事件,加上@Async 注解进行异步处理,防止阻塞业务流程。

    @EventListener
    @Async //异步处理防止阻塞流程
    public void acceptUserRoleId(UserRoleEvent4Id userRoleEvent4Id){
        log.info("开始监听事件,并处理事件结果:"+LocalDateTime.now());
        try {
            String id = userRoleEvent4Id.getId();
            if (StringUtils.isNotEmpty(id)){
                log.info("获取到主键id为:"+ id);
                //先睡10s让业务逻辑走完再查数据,否则查回的为旧数据
                Thread.sleep(10000);
                UserRole userRole = userRoleService.getById(id);
                //先SM3再进行mac加密
                String SM3Digest = csspUtil.getSM3Digest(JSONUtil.toJsonStr(userRole));
                userRole.setMacDigest(csspUtil.getMACDigest(SM3Digest));
                userRoleService.updateById(userRole);
            }
            log.info("结束事件监听:"+LocalDateTime.now());
        }catch (Exception e){
            log.error("事件处理异常:"+e.toString());
            throw new ApiException(new ErrorCode(false,"事件处理出现异常!"));
        }
    }

此处代码中Thread.sleep(10000);进程睡了10s中才查询数据,是为了等业务代码执行完毕,再去查询到最新的数据进行完整性加密处理。

二、对部分重要字段信息进行完整性加密

1.user用户信息实体类:

部分代码如下:

@Slf4j
@TableName("module_sys_user")
@Data
@NoArgsConstructor
@EqualsAndHashCode(callSuper = false)
public class User extends BaseModelUUID {

    private static final long serialVersionUID = 1L;

    @ApiModelProperty(notes = "用户唯一编码")
    private String userIdCode;

    @ApiModelProperty(notes = "用户中文名")
    private String name;

    @ApiModelProperty(notes = "登录英文名")
    private String loginName;

    @ApiModelProperty(notes = "身份证")
    private String idcardNumber;

    @ApiModelProperty(notes = "联系电话")
    private String phone;

  @ApiModelProperty(notes = "mac加密摘要,用于数据完整性校验")
    private String MacDigest;

    @TableField(exist = false)
    private Boolean isMac = false;

    @TableField(exist = false)
    private Boolean isEncrypt = false;

    public String getIdcardNumber() {
        //如果是加密后沒入库,保存数据直接返回密文
        if(isEncrypt) {
            return idcardNumber;
        }
        return ApplicationUtils.getBean(CsspUtil.class).SM4Decrypt(idcardNumber);
    }

    public void setIdcardNumber(String idcardNumber) {
        this.idcardNumber = idcardNumber;
    }

    public void setPhone(String phone) {
        this.phone = phone;
        //生成完整性校验的待加密串
        if (this.isMac){
            String str = this.userIdCode+this.name+this.loginName+this.idcardNumber+this.phone;
            //SM3获取摘要
            String SM3Digest = ApplicationUtils.getBean(CsspUtil.class).getSM3Digest(str);
            //进行mac加密
            String mac = ApplicationUtils.getBean(CsspUtil.class).getMACDigest(SM3Digest);
            log.info("用户信息完整性校验mac值: "+mac);
            this.setMacDigest(mac);
        }
    }
}

2.serverImpl实现类部分代码:

User userInfo = new User();
     userInfo.setUserIdCode(userIdCode);
     userInfo.setName(username);
     userInfo.setLoginName(account);
     userInfo.setStatus(1);
     userInfo.setIsMac(true);
     userInfo.setPhone(mobilenumber);
     this.saveOrUpdate(userInfo);

为了避免其他的业务逻辑也进入到完整性加密的代码逻辑,从而影响代码执行效率,这里定义isMac标识符,当需要对业务数据内容进行完整性加密时,把isMac标识设置为true,进入到加密逻辑中。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值