feat:租户隔离策略

This commit is contained in:
wangyu 2021-12-08 23:47:10 +08:00
parent b9f9cb2612
commit a88776119c
7 changed files with 67 additions and 48 deletions

View File

@ -30,7 +30,7 @@ public class TreeQo<T extends Domain> extends NameLikeQo<T> {
CriteriaBuilder<T> builder = super.criteriaBuilder().with("depth"); CriteriaBuilder<T> builder = super.criteriaBuilder().with("depth");
if (BooleanUtils.isTrue(recursive)) { if (BooleanUtils.isTrue(recursive)) {
builder.with("parentId", "parentIds", Criteria::is) builder.with("parentId", "parentIds", Criteria::is)
.with("parentIds", "parentIds", Criteria::is); .with("parentIds", "parentIds", CriteriaBuilder.Builders.IN);
} else { } else {
builder.with("parentId").with("parentIds", "parentId", CriteriaBuilder.Builders.IN); builder.with("parentId").with("parentIds", "parentId", CriteriaBuilder.Builders.IN);
} }

View File

@ -2,14 +2,14 @@ package com.flyfish.framework.logging.advice;
import com.flyfish.framework.logging.service.LogContext; import com.flyfish.framework.logging.service.LogContext;
import com.flyfish.framework.logging.service.LogManager; import com.flyfish.framework.logging.service.LogManager;
import com.flyfish.framework.utils.UserUtils;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut; import org.aspectj.lang.annotation.Pointcut;
import org.reactivestreams.Publisher; import org.reactivestreams.Publisher;
import org.reactivestreams.Subscriber; import org.springframework.security.core.context.ReactiveSecurityContextHolder;
import org.reactivestreams.Subscription;
import reactor.core.publisher.Flux; import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono; import reactor.core.publisher.Mono;
@ -58,7 +58,7 @@ public class LogAdvice {
context.setError(throwable); context.setError(throwable);
throw throwable; throw throwable;
} finally { } finally {
if (!reactive && context.isValid()) { if (context.isValid() && !reactive) {
context.setResult(result); context.setResult(result);
logManager.tryLog(context.end()); logManager.tryLog(context.end());
} }
@ -73,19 +73,33 @@ public class LogAdvice {
* @param context 上下文 * @param context 上下文
*/ */
private Object handleResult(Object result, LogContext context) { private Object handleResult(Object result, LogContext context) {
if (!context.isValid()) {
return result;
}
Runnable runnable = () -> { Runnable runnable = () -> {
if (context.isValid()) { if (context.isValid()) {
logManager.tryLog(context.end()); logManager.tryLog(context.end());
} }
}; };
if (result instanceof Mono) { if (result instanceof Mono) {
return ((Mono<?>) result) Mono<String> user = ReactiveSecurityContextHolder.getContext()
.map(UserUtils::extractUserName);
return Mono.zip(user, (Mono<?>) result)
.map(value -> {
context.setUser(value.getT1());
return value.getT2();
})
.doOnSuccess(context::setResult) .doOnSuccess(context::setResult)
.doOnError(context::setError) .doOnError(context::setError)
.doOnTerminate(runnable); .doOnTerminate(runnable);
} else if (result instanceof Flux) { } else if (result instanceof Flux) {
return ((Flux<?>) result) Mono<String> user = ReactiveSecurityContextHolder.getContext()
.collectList() .map(UserUtils::extractUserName);
return Mono.zip(user, ((Flux<?>) result).collectList())
.map(value -> {
context.setUser(value.getT1());
return value.getT2();
})
.doOnSuccess(context::setResult) .doOnSuccess(context::setResult)
.doOnError(context::setError) .doOnError(context::setError)
.doOnTerminate(runnable) .doOnTerminate(runnable)
@ -95,31 +109,4 @@ public class LogAdvice {
return result; return result;
} }
} }
@RequiredArgsConstructor
@SuppressWarnings("all")
private static class LogSubscriber implements Subscriber<Object> {
private final LogContext context;
@Override
public void onSubscribe(Subscription s) {
}
@Override
public void onNext(Object o) {
context.setResult(o);
}
@Override
public void onError(Throwable t) {
context.setError(t);
}
@Override
public void onComplete() {
context.end();
}
}
} }

View File

@ -124,7 +124,7 @@ public class LogContext {
} }
public LogContext end() { public LogContext end() {
this.user = UserContext.sharedContext().map(UserContext::currentUser).map(Domain::getName).orElse("未知"); this.user = UserContext.sharedContext().map(UserContext::currentUser).map(Domain::getName).orElse(this.user);
this.endTime = System.currentTimeMillis(); this.endTime = System.currentTimeMillis();
return this; return this;
} }

View File

@ -1,10 +1,18 @@
package com.flyfish.framework.controller; package com.flyfish.framework.controller;
import com.flyfish.framework.bean.Result;
import com.flyfish.framework.controller.reactive.ReactiveTreeController; import com.flyfish.framework.controller.reactive.ReactiveTreeController;
import com.flyfish.framework.domain.AdminUserDetails;
import com.flyfish.framework.domain.DepartmentQo; import com.flyfish.framework.domain.DepartmentQo;
import com.flyfish.framework.domain.po.Department; import com.flyfish.framework.domain.po.Department;
import com.flyfish.framework.utils.UserUtils;
import org.springframework.security.core.context.ReactiveSecurityContextHolder;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Mono;
import java.util.List;
/** /**
* 部分或者校区controller * 部分或者校区controller
@ -13,4 +21,20 @@ import org.springframework.web.bind.annotation.RestController;
@RequestMapping("/departments") @RequestMapping("/departments")
public class DepartmentController extends ReactiveTreeController<Department, DepartmentQo> { public class DepartmentController extends ReactiveTreeController<Department, DepartmentQo> {
/**
* 获取有权限的tree
*
* @return 结果
*/
@GetMapping("authorized-tree")
public Mono<Result<List<Department>>> getAuthorizedTree() {
return ReactiveSecurityContextHolder.getContext()
.map(UserUtils::extractUserDetails)
.flatMap(detail -> {
AdminUserDetails details = (AdminUserDetails) detail;
return reactiveService.getByIds(details.getAuthorityCodes()).collectList();
})
.map(this::makeTree)
.map(Result::accept);
}
} }

View File

@ -28,7 +28,7 @@ public class DepartmentService extends BaseReactiveServiceImpl<Department> {
* @return 结果 * @return 结果
*/ */
public Mono<List<String>> getSubCodes(List<String> parents) { public Mono<List<String>> getSubCodes(List<String> parents) {
Query query = Query.query(Criteria.where("parentId").in(parents)); Query query = Query.query(Criteria.where("parentIds").in(parents));
query.fields().include("_id"); query.fields().include("_id");
return reactiveMongoOperations.find(query, Department.class).map(Department::getId).collectList(); return reactiveMongoOperations.find(query, Department.class).map(Department::getId).collectList();
} }

View File

@ -38,6 +38,7 @@ public class UserDetailsConverter {
if (CollectionUtils.isNotEmpty(user.getDepartments())) { if (CollectionUtils.isNotEmpty(user.getDepartments())) {
val departs = user.getDepartments().stream().map(Department::getId) val departs = user.getDepartments().stream().map(Department::getId)
.collect(Collectors.toList()); .collect(Collectors.toList());
userDetail.setAuthorityCodes(ListUtils.union(departs, userDetail.getAuthorityCodes()));
return departmentService.getSubCodes(departs) return departmentService.getSubCodes(departs)
.map(codes -> { .map(codes -> {
if (CollectionUtils.isNotEmpty(codes)) { if (CollectionUtils.isNotEmpty(codes)) {

View File

@ -8,7 +8,6 @@ import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PathVariable;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono; import reactor.core.publisher.Mono;
import java.util.Collections; import java.util.Collections;
@ -51,20 +50,28 @@ public abstract class ReactiveTreeController<T extends TreeDomain<T>, Q extends
// 第一步查询全部并根据条件筛选 // 第一步查询全部并根据条件筛选
return reactiveService.getList(qo) return reactiveService.getList(qo)
.collectList() .collectList()
.flatMap(filtered -> { .map(this::makeTree)
// 第二步根据父id组成map
Map<String, List<T>> group = filtered.stream()
.collect(Collectors.groupingBy(p -> StringUtils.defaultIfBlank(p.getParentId(), "")));
// 第三步筛选一级树深度
return Flux.fromIterable(filtered)
.filter(item -> null != item && TreeDomain.ROOT.equals(item.getParentId()))
.collectList()
// 第四步根据父id的map填充根tree
.map(topList -> applyChildren(topList, group));
})
.map(Result::accept); .map(Result::accept);
} }
/**
* 构建一个tree
*
* @param filtered 过滤后的节点
* @return 结果
*/
protected List<T> makeTree(List<T> filtered) {
// 第二步根据父id组成map
Map<String, List<T>> group = filtered.stream()
.collect(Collectors.groupingBy(p -> StringUtils.defaultIfBlank(p.getParentId(), "")));
// 第三步筛选一级树深度
List<T> roots = filtered.stream()
.filter(item -> null != item && TreeDomain.ROOT.equals(item.getParentId()))
.collect(Collectors.toList());
// 第四步根据父id的map填充根tree
return applyChildren(roots, group);
}
/** /**
* 递归赋值儿子们 * 递归赋值儿子们
* *