Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(认证模块): 修复更新不存在的角色可能报错问题 #368

Merged
merged 1 commit into from
Jul 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.jetlinks.community.auth.dimension;

import lombok.AllArgsConstructor;
import lombok.RequiredArgsConstructor;
import org.apache.commons.collections4.CollectionUtils;
import org.hswebframework.ezorm.rdb.mapping.ReactiveQuery;
import org.hswebframework.ezorm.rdb.mapping.ReactiveRepository;
Expand All @@ -11,23 +12,29 @@
import org.hswebframework.web.crud.events.EntityDeletedEvent;
import org.hswebframework.web.crud.events.EntityModifyEvent;
import org.hswebframework.web.crud.events.EntitySavedEvent;
import org.hswebframework.web.crud.utils.TransactionUtils;
import org.hswebframework.web.system.authorization.api.entity.DimensionUserEntity;
import org.hswebframework.web.system.authorization.api.event.ClearUserAuthorizationCacheEvent;
import org.hswebframework.web.system.authorization.defaults.service.DefaultDimensionUserService;
import org.hswebframework.web.system.authorization.defaults.service.terms.DimensionTerm;
import org.reactivestreams.Publisher;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.event.EventListener;
import org.springframework.core.GenericTypeResolver;
import org.springframework.transaction.reactive.TransactionSynchronization;
import org.springframework.util.StringUtils;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import javax.annotation.Nonnull;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

@AllArgsConstructor
@RequiredArgsConstructor
public abstract class BaseDimensionProvider<T extends GenericEntity<String>> implements DimensionProvider {

protected final ReactiveRepository<T, String> repository;
Expand All @@ -36,6 +43,8 @@ public abstract class BaseDimensionProvider<T extends GenericEntity<String>> imp

protected final DefaultDimensionUserService dimensionUserService;

private Class<?> entityType;

protected abstract DimensionType getDimensionType();

protected abstract Mono<Dimension> convertToDimension(T entity);
Expand All @@ -49,8 +58,7 @@ public Flux<? extends Dimension> getDimensionByUserId(String s) {
return DimensionTerm
.inject(createQuery(), "id", getDimensionType().getId(), Collections.singletonList(s))
.fetch()
.as(this::convertToDimension)
;
.as(this::convertToDimension);
}

@Override
Expand Down Expand Up @@ -93,46 +101,96 @@ public Flux<? extends DimensionType> getAllType() {
return Flux.just(getDimensionType());
}

protected Class<?> getEntityType() {
return entityType == null
? entityType = GenericTypeResolver.resolveTypeArgument(this.getClass(), BaseDimensionProvider.class)
: entityType;
}

private boolean isNotSameType(Class<?> type) {
Class<?> genType = getEntityType();

return genType == null || !genType.isAssignableFrom(type);
}


@EventListener
public void handleEvent(EntityDeletedEvent<T> event) {
if (isNotSameType(event.getEntityType())) {
return;
}
event.async(
clearUserAuthenticationCache(event.getEntity())
);
}

@EventListener
public void handleEvent(EntitySavedEvent<T> event) {
if (isNotSameType(event.getEntityType())) {
return;
}
event.async(
clearUserAuthenticationCache(event.getEntity())
);
}

@EventListener
public void handleEvent(EntityModifyEvent<T> event) {
event.async(
clearUserAuthenticationCache(event.getAfter())
);
}
if (isNotSameType(event.getEntityType())) {
return;
}
Map<String, T> beforeMap = event
.getBefore()
.stream()
.collect(Collectors.toMap(T::getId, Function.identity()));

private Mono<Void> clearUserAuthenticationCache(Collection<T> roles) {
List<String> idList = roles
List<T> readyToClear = event
.getAfter()
.stream()
.map(GenericEntity::getId)
.filter(StringUtils::hasText)
.filter(after -> isChanged(beforeMap.get(after.getId()), after))
.collect(Collectors.toList());
if (CollectionUtils.isEmpty(idList)) {
return Mono.empty();

if (readyToClear.isEmpty()) {
return;
}
return dimensionUserService
.createQuery()
.where()
.in(DimensionUserEntity::getDimensionId, idList)
.and(DimensionUserEntity::getDimensionTypeId, getDimensionType().getId())
.fetch()
.map(DimensionUserEntity::getUserId)
.collectList()
.filter(CollectionUtils::isNotEmpty)
.flatMap(users->ClearUserAuthorizationCacheEvent.of(users).publish(eventPublisher));
event.async(
clearUserAuthenticationCache(readyToClear)
);
}

protected boolean isChanged(T before, T after) {
return true;
}


private Mono<Void> clearUserAuthenticationCache0(Collection<T> entities) {
return Flux
.fromIterable(entities)
.mapNotNull(GenericEntity::getId)
.buffer(200)
.flatMap(list -> dimensionUserService
.createQuery()
.where()
.select(DimensionUserEntity::getUserId)
.in(DimensionUserEntity::getDimensionId, list)
.and(DimensionUserEntity::getDimensionTypeId, getDimensionType().getId())
.fetch()
.map(DimensionUserEntity::getUserId)
.collect(Collectors.toSet())
.filter(CollectionUtils::isNotEmpty)
.flatMap(users -> ClearUserAuthorizationCacheEvent.of(users).publish(eventPublisher)))
.then();
}

protected Mono<Void> clearUserAuthenticationCache(Collection<T> entities) {
return TransactionUtils
.registerSynchronization(new TransactionSynchronization() {
@Override
@Nonnull
public Mono<Void> afterCommit() {
return clearUserAuthenticationCache0(entities);
}
}, TransactionSynchronization::afterCommit);
}

}
Original file line number Diff line number Diff line change
@@ -1,24 +1,41 @@
package org.jetlinks.community.auth.dimension;

import org.apache.commons.collections4.CollectionUtils;
import org.hswebframework.ezorm.rdb.mapping.ReactiveQuery;
import org.hswebframework.ezorm.rdb.mapping.ReactiveRepository;
import org.hswebframework.web.authorization.DefaultDimensionType;
import org.hswebframework.web.authorization.Dimension;
import org.hswebframework.web.authorization.DimensionType;
import org.hswebframework.web.cache.ReactiveCache;
import org.hswebframework.web.cache.ReactiveCacheManager;
import org.hswebframework.web.crud.events.EntityDeletedEvent;
import org.hswebframework.web.crud.events.EntityModifyEvent;
import org.hswebframework.web.crud.events.EntitySavedEvent;
import org.hswebframework.web.system.authorization.defaults.service.DefaultDimensionUserService;
import org.jetlinks.community.auth.entity.MenuEntity;
import org.jetlinks.community.auth.entity.RoleEntity;
import org.jetlinks.community.auth.enums.RoleState;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.util.Collection;
import java.util.Objects;

@Component
public class RoleDimensionProvider extends BaseDimensionProvider<RoleEntity> {

private final ReactiveCache<Dimension> cache;

public RoleDimensionProvider(ReactiveRepository<RoleEntity, String> repository,
DefaultDimensionUserService dimensionUserService,
ApplicationEventPublisher eventPublisher) {
ApplicationEventPublisher eventPublisher,
ReactiveCacheManager cacheManager) {
super(repository, eventPublisher, dimensionUserService);
this.cache = cacheManager.getCache("role-dimension");
}

@Override
Expand All @@ -32,6 +49,67 @@ protected Mono<Dimension> convertToDimension(RoleEntity entity) {
return Mono.just(entity.toDimension());
}

@Override
protected Class<?> getEntityType() {
return RoleEntity.class;
}

@EventListener
public void handleMenuChanged(EntitySavedEvent<MenuEntity> event) {
event.async(cleanMenuCache(event.getEntity()));
}

@EventListener
public void handleMenuChanged(EntityModifyEvent<MenuEntity> event) {
event.async(cleanMenuCache(event.getAfter()));
}

@EventListener
public void handleMenuChanged(EntityDeletedEvent<MenuEntity> event) {
event.async(cleanMenuCache(event.getEntity()));
}

@Override
@EventListener
public void handleEvent(EntitySavedEvent<RoleEntity> event) {
super.handleEvent(event);
event.async(cleanCache(event.getEntity()));
}

@Override
@EventListener
public void handleEvent(EntityModifyEvent<RoleEntity> event) {
super.handleEvent(event);
event.async(cleanCache(event.getAfter()));
}

@Override
protected boolean isChanged(RoleEntity before, RoleEntity after) {
//名称
return !Objects.equals(before.getName(), after.getName())
|| !Objects.equals(before.getState(), after.getState());
}

@Override
@EventListener
public void handleEvent(EntityDeletedEvent<RoleEntity> event) {
super.handleEvent(event);
event.async(cleanCache(event.getEntity()));
}

private Mono<Void> cleanMenuCache(Collection<MenuEntity> menuBinds) {
return cache.clear();
}

private Mono<Void> cleanCache(Collection<RoleEntity> roleId) {
return Flux.fromIterable(roleId)
.map(RoleEntity::getId)
.filter(StringUtils::hasText)
.collectList()
.filter(CollectionUtils::isNotEmpty)
.flatMap(cache::evictAll);
}

@Override
protected ReactiveQuery<RoleEntity> createQuery() {
return super
Expand Down