springboot~为ES实体封装审计Auditing功能
审记功能在Jpa框架里出现的,主要针对实体的几个字段进行自动化的赋值,让业务人员可以把关注点放在业务上,对于公用的,有规则的字段,由系统帮我们去处理。
原理
通过spring aop功能实现对es仓库接口方法的拦截,然后在方法处理之前,为实体的这些公用字段赋值即可,我们使用了jpa里的一些注解,如@CreateBy,@CreateDate,@LatestModifyDate等等。
EsBaseEntity实体
@Data
public class EsBaseEntity {
public static final String dateTimeFormat = "yyyy/MM/dd||yyyy-MM-dd" +
"||yyyy-MM-dd HH:mm:ss||yyyy/MM/dd HH:mm:ss" +
"||yyyy-MM-dd HH:mm:ss.SSS||yyyy/MM/dd HH:mm:ss.SSS" +
"||yyyy-MM-dd'T'HH:mm:ss.SSS";
/**
* 创建时间.
*/
@Field(type = FieldType.Date, format = DateFormat.custom, pattern = dateTimeFormat)
@CreatedDate
protected String createTime;
/**
* 创建人.
*/
@Field(type = FieldType.Keyword)
@CreatedBy
protected String creator;
/**
* 更新时间.
*/
@Field(type = FieldType.Date, format = DateFormat.custom, pattern = dateTimeFormat)
@LastModifiedDate
protected String updateTime;
/**
* 更新人.
*/
@Field(type = FieldType.Keyword)
@LastModifiedBy
protected String updateUser;
/**
* 删除标记.
*/
@Field(type = FieldType.Keyword)
protected boolean delFlag;
/**
* 主键.
*/
@Id
private String id = String.valueOf(SnowFlakeUtil.getFlowIdInstance().nextId());
}
审记拦截器
@Component
@Aspect
public class AuditAspect {
DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
/**
* 添加ES实体-切入点
*/
@Pointcut("execution(* org.springframework.data.repository.CrudRepository.save(..))")
public void save() {
}
/**
* 更新ES实体-切入点
*/
@Pointcut("execution(* org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate.update(..))")
public void update() {
}
/**
* 插入实体拦截器.
*
* @param joinPoint
* @throws IllegalAccessException
*/
@Before("save()")
public void beforeSave(JoinPoint joinPoint) throws IllegalAccessException {
System.out.println("插入拦截");
if (joinPoint.getArgs().length > 0) {
Object esBaseEntity = joinPoint.getArgs()[0];
Field[] fields = ClassHelper.getAllFields(esBaseEntity.getClass());
List<Field> fieldList = Arrays.stream(fields)
.filter(o -> o.getAnnotation(CreatedDate.class) != null
|| o.getAnnotation(LastModifiedDate.class) != null)
.collect(Collectors.toList());
if (!CollectionUtils.isEmpty(fieldList)) {
for (Field field : fieldList) {
field.setAccessible(true);//取消私有字段限制
if (field.get(esBaseEntity) == null) {
field.set(esBaseEntity, df.format(new Date()));
}
}
}
List<Field> auditFieldList = Arrays.stream(fields)
.filter(o -> o.getAnnotation(CreatedBy.class) != null
|| o.getAnnotation(LastModifiedBy.class) != null)
.collect(Collectors.toList());
if (!CollectionUtils.isEmpty(auditFieldList)) {
for (Field field : auditFieldList) {
field.setAccessible(true);//取消私有字段限制
if (field.get(esBaseEntity) == null) {
EsAuditorAware esAuditorAware = SpringContextConfig.getBean(EsAuditorAware.class);
if (esAuditorAware != null) {
field.set(esBaseEntity, esAuditorAware.getCurrentAuditor().orElse(null));
}
}
}
}
}
}
/**
* 更新实体拦截器.
*
* @param joinPoint
*/
@Before("update()")
public void beforeUpdate(JoinPoint joinPoint) {
System.out.println("更新拦截");
if (joinPoint.getArgs().length == 1 && joinPoint.getArgs()[0] instanceof UpdateQuery) {
UpdateQuery updateQuery = (UpdateQuery) joinPoint.getArgs()[0];
Map source = updateQuery.getUpdateRequest().doc().sourceAsMap();
Field[] fields = ClassHelper.getAllFields(updateQuery.getClazz());
List<Field> fieldList = Arrays.stream(fields)
.filter(o -> o.getAnnotation(LastModifiedDate.class) != null)
.collect(Collectors.toList());
for (Field field : fieldList) {
if (!source.containsKey(field.getName())) {
source.put(field.getName(), df.format(new Date()));
}
}
List<Field> auditFieldList = Arrays.stream(fields)
.filter(o -> o.getAnnotation(LastModifiedBy.class) != null)
.collect(Collectors.toList());
for (Field field : auditFieldList) {
if (!source.containsKey(field.getName())) {
EsAuditorAware esAuditorAware = SpringContextConfig.getBean(EsAuditorAware.class);
if (esAuditorAware != null) {
source.put(field.getName(), esAuditorAware.getCurrentAuditor().orElse(null));
}
}
}
updateQuery.getUpdateRequest().doc().source(source);
}
}
}
对审记人使用Aware方式实现
/**
* Es获取审核当前对象.
*
* @param <T>
*/
public interface EsAuditorAware<T> {
Optional<T> getCurrentAuditor();
}
/**
* 得到当前操作人信息.
*/
@Component
public class UserAuditorAware implements EsAuditorAware<String> {
@Override
public Optional<String> getCurrentAuditor() {
return Optional.of("1");
}
}
第三方组件开启审计功能
/**
* 开启ES审计功能.
* 主要对建立人、建立时间、更新人、更新时间赋值.
*/
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import({AuditAspect.class, SpringContextConfig.class})
public @interface EnableEsAuditing {
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
2019-08-05 feign响应Image流对象
2015-08-05 AngularJS~集成的ajax和服务的注入
2014-08-05 说说设计模式~装饰器模式(Decorator)
2014-08-05 说说设计模式~观察者模式(Observer)
2013-08-05 我心中的核心组件(可插拔的AOP)~第二回 缓存拦截器