当系统中有审计需求时,特别是需要对某些数据进行动态监控时,我们可以使用EntityentiListeners来实现,当然这是基于使用JPA而不是mybatis的情况下。

当前我们的需求场景:

1.需要监控某一个实体的数据变化(add,update,delete)

2.需要记录:id,who,when, action, entity,condition,value分别表示id,操作人,操作时间,动作(add,update,delete),实体名称,状态(before:操作前,after:操作后),值

如何做?

1.如何识别add,update,delete操作?

  entityListenners有定义的@PreUpdate:更新前,@PostUpdate:更新后,@PrePersist:保存前,@PostPersist:保存后,@PreRemove:删除前。

  我们可以在保存前获取保存的数据,记录为add新增操作数据,我们在删除前获取数据,记录为删除的数据,我们在更新前,获取当前数据为更新后的数据,另外根据id从数据库获取之前的数据作为之前的数据,此时加以对比,若存在差异则为更新。

ps:如何区分update和add?updata时实体中是存在id的,add时不存在。

2.如何获取当前操作人:entityListenner和AOP有点不一样,需要在启动类中加上下列语句:

@Bean
public MethodInvokingFactoryBean methodInvokingFactoryBean() {
MethodInvokingFactoryBean methodInvokingFactoryBean = new MethodInvokingFactoryBean();
methodInvokingFactoryBean.setTargetClass(SecurityContextHolder.class);
methodInvokingFactoryBean.setTargetMethod("setStrategyName");
methodInvokingFactoryBean.setArguments(new String[]{SecurityContextHolder.MODE_INHERITABLETHREADLOCAL});
return methodInvokingFactoryBean;
}

然后使用spring security工具获取当前人,类似这样(具体根据本系统具体情况定制):

public Optional<String> getCurrentAuditor() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
String reString=(String)((JwtAuthenticationToken)authentication).getToken().getClaims().get("preferred_username");
return Optional.of(reString);
}

代码实现:

1.添加自己的listener类,实现监控,存日志逻辑

package com.b.pos.quotation.listeners;

import java.time.Instant;
import java.util.Optional;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; import javax.persistence.PostPersist;
import javax.persistence.PostUpdate;
import javax.persistence.PrePersist;
import javax.persistence.PreRemove;
import javax.persistence.PreUpdate; import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component; import com.bmw.pos.quotation.domain.InterfaceLog;
import com.bmw.pos.quotation.domain.Quotation;
import com.bmw.pos.quotation.repository.InterfaceLogRepository;
import com.bmw.pos.quotation.security.SecurityUtils;
import com.bmw.pos.quotation.service.QuotationService;
import com.bmw.pos.quotation.util.ProfileUtil;
import com.google.common.base.Throwables; /**
* Application status Listener
*/
@Component
public class QuotationStausListener {
private static final Logger log = LoggerFactory.getLogger(QuotationStausListener.class); private static InterfaceLogRepository interfaceLogRepository; private static QuotationService quotationService; @Autowired
public synchronized void setInterfaceLogRepository(InterfaceLogRepository interfaceLogRepository) {
QuotationStausListener.interfaceLogRepository = interfaceLogRepository;
} @Autowired
public synchronized void setQuotationService(QuotationService quotationService) {
QuotationStausListener.quotationService = quotationService;
} /**
* after save success
*
* @param object
*/
@PostPersist
public void postpersist(Object object) {
} /**
* after update success
*
* @param object
*/
@PostUpdate
public void postUpdate(Object object) {
} @PreRemove
public void beforeRemove(Object object) {
log.info("@PreRemove");
Quotation quotation = (Quotation) object;
cachedThreadPool.execute(new Runnable() {
@Override
public void run() {
saveInterfaceLog(quotation.getId().toString(), quotation.toString(),"Quotation","Delete","Before");
}
});
} @PreUpdate
public void beforeUpdate(Object object) {
log.info("@PreUpdate");
try {
Quotation quotation = (Quotation) object;
log.info("@PreUpdate--------->Quotation: {}",quotation.toString());
Quotation oldData = quotationService.findOldData(quotation.getId());
log.info("oldData.get()------->oldData.get(): {}",oldData.toString());
if(oldData!=null&&(!quotation.equals(oldData))) {
cachedThreadPool.execute(new Runnable() {
@Override
public void run() {
saveInterfaceLog(quotation.getId().toString(), oldData.toString(),"Quotation","Update","Before");
saveInterfaceLog(quotation.getId().toString(), quotation.toString(),"Quotation","Update","After");
}
});
}
} catch (Exception e) {
log.info(Throwables.getStackTraceAsString(e));
} } @PrePersist
public void beforeSave(Object object) {
log.info("@PrePersist");
Quotation quotation = (Quotation) object;
cachedThreadPool.execute(new Runnable() {
@Override
public void run() {
saveInterfaceLog(quotation.getId().toString(), quotation.toString(),"Quotation","Add","");
}
});
} ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
private String getCurrentUserName() {
Optional<String> user=SecurityUtils.getCurrentUserLogin();
return user.isPresent() ? user.get() : null;
}
/**
* save log
*
* @param appNo
* @param requestData
* @param response
* @param exceptions
*/
private void saveInterfaceLog(String quotationNo, String curData,String entityName,String action,String condition) {
// save log
InterfaceLog interfaceLog = new InterfaceLog();
interfaceLog.setIntType(entityName);
interfaceLog.setReturnParam(curData);
interfaceLog.setCalledTime(Instant.now());
String channel = ProfileUtil.getChannelByProfile();
interfaceLog.setEntityType(channel);
interfaceLog.setAppGlobalId(quotationNo);
interfaceLog.setEntityType(action);
interfaceLog.setEntityCode(condition);
interfaceLog.setInputParam(getCurrentUserName());
log.info("InterfaceLog: {}",interfaceLog.toString());
QuotationStausListener.interfaceLogRepository.saveAndFlush(interfaceLog); } }

2.为实体添加监控注解:@EntityListeners({AuditingEntityListener.class,QuotationStausListener.class})

  重写equals方法,使用 eclipse自带generator生成即可

3.创建数据表

----------------------分割线------------------------------

看一下效果:

最新文章

  1. Chrome 中的 JavaScript 断点设置和调试技巧
  2. 第一章:Android系统移植与驱动开发概述
  3. 清除路由器NAT地址转换
  4. kqueue用法简介
  5. HDU 5833 Zhu and 772002 (数论+高斯消元)
  6. 代码审查工具StyleCop
  7. IIS7下,flush无效,解决方案
  8. C#下内存管理--垃圾收集
  9. JSP中使用的模式——JSP+JavaBean
  10. HTML之学习笔记(二)颜色体系
  11. 移动端audio自动播放问题
  12. hdu 2044 递推
  13. vfd with stm8
  14. Java编程思想(前十章)
  15. Flink Event Time Processing and Watermarks(文末有翻译)
  16. Matplotlib学习---用seaborn画联合分布图(joint plot)
  17. Chimee - 简单易用的H5视频播放器解决方案
  18. java中获取两个时间中的每一天
  19. CodeForces - 444C
  20. c++文件中引用C代码

热门文章

  1. Redis 设计与实现 5:压缩列表
  2. Spring Boot GraphQL 实战 02_增删改查和自定义标量
  3. 图解HTTP权威指南(三)| Web服务器对HTTP请求的处理和响应
  4. webapplicationContext之ServletContext等相关概念说明
  5. kafka-spark偏移量提交至redis kafka1.0版本
  6. Spring Boot 2.x基础教程:多个文件的上传
  7. Azure Terraform(三)部署 Web 应用程序
  8. 在kotlin用jni调用c++的dll中踩的坑
  9. [Java基础]——String类
  10. VMware 16.1虚拟机安装