如何使 AuditorAware 与 Spring 数据 Mongo 响应式一起工作

How to make AuditorAware work with Spring Data Mongo Reactive

Spring Security 5提供了一个,但是当我想实现AuditorAware并自动获取试听工作时,却没有用。 目前我找不到 AuditorAwareReactive 变体。

@Bean
public AuditorAware<Username> auditor() {
    return () -> ReactiveSecurityContextHolder.getContext()
        .map(SecurityContext::getAuthentication)
        .log()
        .filter(a -> a != null && a.isAuthenticated())
        .map(Authentication::getPrincipal)
        .cast(UserDetails.class)
        .map(auth -> new Username(auth.getName()))
        .switchIfEmpty(Mono.empty())
        .blockOptional();
}

我已经在我的引导中添加了 @EnableMongoAuduting Application class。

在 Mongo 文档上 class。我添加了试听相关的注释。

@CreatedDate
private LocalDateTime createdDate;

@CreatedBy
private Username author;

当我添加一个post时,createdDate被填充,但作者为空

{"id":"5a49ccdb9222971f40a4ada1","title":"my first post","content":"content of my first post","createdDate":"2018-01-01T13:53:31.234","author":null}

完整代码为here,基于Spring Boot 2.0.0.M7.

更新: Spring 引导 2.4.0-M2/Spring 数据通用 2.4.0-M2/Spring 数据 Mongo 3.1 .0-M2包含一个ReactiveAuditorAware,检查this new sample注意:使用@EnableReactiveMongoAuditing激活它。

要填充 createdBy 属性,您需要 link 您的 auditorAware bean 带有注解 @EnableMongoAuditing

在您的 MongoConfig class 中,定义您的 bean :

@Bean(name = "auditorAware")
public AuditorAware<String> auditor() {
    ....
}

并在注释中使用它:

@Configuration
@EnableMongoAuditing(auditorAwareRef="auditorAware")
class MongoConfig {
    ....
}

已弃用:更新方案见原文post

在提供官方 reactive AuditAware 之前,有一种替代方法可以通过 Spring 数据 Mongo 具体 ReactiveBeforeConvertCallback 来实现这些。

  1. 不使用@EnableMongoAuditing
  2. 实现您自己的ReactiveBeforeConvertCallback,这里我为那些需要审计的实体使用PersistentEntity接口。
public class PersistentEntityCallback implements ReactiveBeforeConvertCallback<PersistentEntity> {

    @Override
    public Publisher<PersistentEntity> onBeforeConvert(PersistentEntity entity, String collection) {
        var user = ReactiveSecurityContextHolder.getContext()
                .map(SecurityContext::getAuthentication)
                .filter(it -> it != null && it.isAuthenticated())
                .map(Authentication::getPrincipal)
                .cast(UserDetails.class)
                .map(userDetails -> new Username(userDetails.getUsername()))
                .switchIfEmpty(Mono.empty());

        var currentTime = LocalDateTime.now();

        if (entity.getId() == null) {
            entity.setCreatedDate(currentTime);
        }
        entity.setLastModifiedDate(currentTime);

        return user
                .map(u -> {
                            if (entity.getId() == null) {
                                entity.setCreatedBy(u);
                            }
                            entity.setLastModifiedBy(u);

                            return entity;
                        }
                )
                .defaultIfEmpty(entity);
    }
}

查看完整代码here

我正在发布另一个解决方案,该解决方案使用输入 ID 来支持更新操作:

@Component
@RequiredArgsConstructor
public class AuditCallback implements ReactiveBeforeConvertCallback<AuditableEntity> {

    private final ReactiveMongoTemplate mongoTemplate;

    private Mono<?> exists(Object id, Class<?> entityClass) {
        if (id == null) {
            return Mono.empty();
        }
        return mongoTemplate.findById(id, entityClass);
    }

    @Override
    public Publisher<AuditableEntity> onBeforeConvert(AuditableEntity entity, String collection) {
        var securityContext = ReactiveSecurityContextHolder.getContext();
        return securityContext
                .zipWith(exists(entity.getId(), entity.getClass()))
                .map(tuple2 -> {
                    var auditableEntity = (AuditableEntity) tuple2.getT2();
                    auditableEntity.setLastModifiedBy(tuple2.getT1().getAuthentication().getName());
                    auditableEntity.setLastModifiedDate(Instant.now());
                    return auditableEntity;
                })
                .switchIfEmpty(Mono.zip(securityContext, Mono.just(entity))
                        .map(tuple2 -> {
                            var auditableEntity = (AuditableEntity) tuple2.getT2();
                            String principal = tuple2.getT1().getAuthentication().getName();
                            Instant now = Instant.now();
                            auditableEntity.setLastModifiedBy(principal);
                            auditableEntity.setCreatedBy(principal);
                            auditableEntity.setLastModifiedDate(now);
                            auditableEntity.setCreatedDate(now);
                            return auditableEntity;
                        }));
    }
}