mirror of
https://github.com/hs-web/hsweb-framework.git
synced 2026-06-05 20:33:23 +08:00
增加维度数据权限
This commit is contained in:
@@ -70,6 +70,7 @@ public class AopAuthorizingController extends StaticMethodMatcherPointcutAdvisor
|
||||
context.setAuthentication(auth);
|
||||
Function<Runnable, Publisher> afterRuner = runnable -> {
|
||||
MethodInterceptorContext interceptorContext = holder.createParamContext(invoker.get());
|
||||
context.setParamContext(interceptorContext);
|
||||
runnable.run();
|
||||
return (Publisher<?>) interceptorContext.getInvokeResult();
|
||||
};
|
||||
|
||||
@@ -163,6 +163,7 @@ public class DefaultBasicAuthorizeDefinition implements AopAuthorizeDefinition {
|
||||
resource.setId(ann.id());
|
||||
resource.setName(ann.name());
|
||||
resource.setLogical(ann.logical());
|
||||
resource.setPhased(ann.phased());
|
||||
resource.setDescription(String.join("\n", ann.description()));
|
||||
for (ResourceAction action : ann.actions()) {
|
||||
putAnnotation(resource, action);
|
||||
@@ -193,6 +194,7 @@ public class DefaultBasicAuthorizeDefinition implements AopAuthorizeDefinition {
|
||||
typeDefinition.setId(dataAccessType.id());
|
||||
typeDefinition.setName(dataAccessType.name());
|
||||
typeDefinition.setController(dataAccessType.controller());
|
||||
typeDefinition.setConfiguration(dataAccessType.configuration());
|
||||
typeDefinition.setDescription(String.join("\n", dataAccessType.description()));
|
||||
}
|
||||
definition.getDataAccess()
|
||||
@@ -205,6 +207,7 @@ public class DefaultBasicAuthorizeDefinition implements AopAuthorizeDefinition {
|
||||
typeDefinition.setId(dataAccessType.id());
|
||||
typeDefinition.setName(dataAccessType.name());
|
||||
typeDefinition.setController(dataAccessType.controller());
|
||||
typeDefinition.setConfiguration(dataAccessType.configuration());
|
||||
typeDefinition.setDescription(String.join("\n", dataAccessType.description()));
|
||||
definition.getDataAccess()
|
||||
.getDataAccessTypes()
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
package org.hswebframework.web.authorization.basic.handler.access;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.hswebframework.ezorm.rdb.mapping.ReactiveRepository;
|
||||
import org.hswebframework.utils.ClassUtils;
|
||||
import org.hswebframework.web.aop.MethodInterceptorContext;
|
||||
import org.hswebframework.web.authorization.Authentication;
|
||||
import org.hswebframework.web.authorization.Dimension;
|
||||
import org.hswebframework.web.authorization.DimensionType;
|
||||
import org.hswebframework.web.authorization.define.AuthorizingContext;
|
||||
import org.hswebframework.web.crud.web.reactive.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
public class DataAccessHandlerContext {
|
||||
|
||||
private Class<?> entityType;
|
||||
|
||||
private ReactiveRepository<?, Object> repository;
|
||||
|
||||
private Authentication authentication;
|
||||
|
||||
private List<Dimension> dimensions;
|
||||
|
||||
private MethodInterceptorContext paramContext;
|
||||
|
||||
public static DataAccessHandlerContext of(AuthorizingContext context, String type) {
|
||||
DataAccessHandlerContext requestContext = new DataAccessHandlerContext();
|
||||
Authentication authentication = context.getAuthentication();
|
||||
requestContext.setDimensions(authentication.getDimensions(type));
|
||||
requestContext.setAuthentication(context.getAuthentication());
|
||||
requestContext.setParamContext(context.getParamContext());
|
||||
|
||||
Object target = context.getParamContext().getTarget();
|
||||
Class entityType = ClassUtils.getGenericType(org.springframework.util.ClassUtils.getUserClass(target));
|
||||
if (entityType != Object.class) {
|
||||
requestContext.setEntityType(entityType);
|
||||
}
|
||||
|
||||
if (target instanceof ReactiveQueryController) {
|
||||
requestContext.setRepository(((ReactiveQueryController) target).getRepository());
|
||||
} else if (target instanceof ReactiveSaveController) {
|
||||
requestContext.setRepository(((ReactiveSaveController) target).getRepository());
|
||||
} else if (target instanceof ReactiveDeleteController) {
|
||||
requestContext.setRepository(((ReactiveDeleteController) target).getRepository());
|
||||
} else if (target instanceof ReactiveServiceQueryController) {
|
||||
requestContext.setRepository(((ReactiveServiceQueryController) target).getService().getRepository());
|
||||
} else if (target instanceof ReactiveServiceSaveController) {
|
||||
requestContext.setRepository(((ReactiveServiceSaveController) target).getService().getRepository());
|
||||
} else if (target instanceof ReactiveServiceDeleteController) {
|
||||
requestContext.setRepository(((ReactiveServiceDeleteController) target).getService().getRepository());
|
||||
}
|
||||
// TODO: 2019-11-18 not reactive implements
|
||||
|
||||
return requestContext;
|
||||
}
|
||||
}
|
||||
@@ -30,7 +30,8 @@ public final class DefaultDataAccessController implements DataAccessController {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
this.parent = parent;
|
||||
addHandler(new FieldFilterDataAccessHandler());
|
||||
addHandler(new FieldFilterDataAccessHandler())
|
||||
.addHandler(new DimensionDataAccessHandler());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,382 @@
|
||||
package org.hswebframework.web.authorization.basic.handler.access;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.SneakyThrows;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.hswebframework.ezorm.core.param.Param;
|
||||
import org.hswebframework.ezorm.core.param.QueryParam;
|
||||
import org.hswebframework.web.api.crud.entity.Entity;
|
||||
import org.hswebframework.web.api.crud.entity.QueryParamEntity;
|
||||
import org.hswebframework.web.authorization.Authentication;
|
||||
import org.hswebframework.web.authorization.Dimension;
|
||||
import org.hswebframework.web.authorization.Permission;
|
||||
import org.hswebframework.web.authorization.access.DataAccessConfig;
|
||||
import org.hswebframework.web.authorization.access.DataAccessHandler;
|
||||
import org.hswebframework.web.authorization.annotation.DimensionDataAccess;
|
||||
import org.hswebframework.web.authorization.define.AuthorizingContext;
|
||||
import org.hswebframework.web.authorization.exception.AccessDenyException;
|
||||
import org.hswebframework.web.authorization.simple.DimensionDataAccessConfig;
|
||||
import org.hswebframework.web.bean.FastBeanCopier;
|
||||
import org.hswebframework.web.utils.AnnotationUtils;
|
||||
import org.reactivestreams.Publisher;
|
||||
import org.springframework.core.annotation.AnnotatedElementUtils;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
@Slf4j
|
||||
public class DimensionDataAccessHandler implements DataAccessHandler {
|
||||
@Override
|
||||
public boolean isSupport(DataAccessConfig access) {
|
||||
return access instanceof DimensionDataAccessConfig;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean handle(DataAccessConfig access, AuthorizingContext context) {
|
||||
DimensionDataAccessConfig config = ((DimensionDataAccessConfig) access);
|
||||
DataAccessHandlerContext requestContext = DataAccessHandlerContext.of(context, config.getScopeType());
|
||||
if (!checkSupported(config, requestContext)) {
|
||||
return false;
|
||||
}
|
||||
switch (access.getAction()) {
|
||||
case Permission.ACTION_QUERY:
|
||||
case Permission.ACTION_GET:
|
||||
return doHandleQuery(config, requestContext);
|
||||
case Permission.ACTION_ADD:
|
||||
case Permission.ACTION_SAVE:
|
||||
case Permission.ACTION_UPDATE:
|
||||
return doHandleUpdate(config, requestContext);
|
||||
case Permission.ACTION_DELETE:
|
||||
return doHandleDelete(config, requestContext);
|
||||
default:
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("data access [{}] not support for {}", config.getType().getId(), access.getAction());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
protected String getProperty(DimensionDataAccessConfig cfg,
|
||||
DataAccessHandlerContext ct) {
|
||||
return Optional.ofNullable(
|
||||
getMappingInfo(ct).get(cfg.getScopeType()))
|
||||
.map(MappingInfo::getProperty)
|
||||
.orElseGet(() -> {
|
||||
log.warn("{} not supported dimension data access", ct.getParamContext().getMethod());
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
protected boolean checkSupported(DimensionDataAccessConfig cfg, DataAccessHandlerContext ctx) {
|
||||
Authentication authentication = ctx.getAuthentication();
|
||||
|
||||
/*
|
||||
DataAccessHelper.assert()
|
||||
*/
|
||||
if (CollectionUtils.isEmpty(ctx.getDimensions())) {
|
||||
log.warn("user:[{}] dimension not setup", authentication.getUser().getId());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!getMappingInfo(ctx).containsKey(cfg.getScopeType())) {
|
||||
log.warn("{} not supported dimension data access.see annotation: @DimensionDataAccess", ctx.getParamContext().getMethod());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected boolean doHandleDelete(DimensionDataAccessConfig cfg,
|
||||
DataAccessHandlerContext context) {
|
||||
|
||||
|
||||
// TODO: 2019-11-18
|
||||
return doHandleUpdate(cfg, context);
|
||||
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
protected Object handleUpdateById(DimensionDataAccessConfig config,
|
||||
DataAccessHandlerContext context,
|
||||
MappingInfo mappingInfo,
|
||||
Object id) {
|
||||
List<Dimension> dimensions = context.getDimensions();
|
||||
|
||||
Set<Object> scope = CollectionUtils.isNotEmpty(config.getScope()) ?
|
||||
config.getScope() :
|
||||
dimensions
|
||||
.stream()
|
||||
.map(Dimension::getId)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
Function<Collection<Object>, Mono<Void>> reactiveCheck = obj -> context
|
||||
.getRepository()
|
||||
.findById(obj)
|
||||
.doOnNext(r -> {
|
||||
Object val = FastBeanCopier.copy(r, new HashMap<>(), FastBeanCopier.include(mappingInfo.getProperty()))
|
||||
.get(mappingInfo.getProperty());
|
||||
if (!StringUtils.isEmpty(val)
|
||||
&& !scope.contains(val)) {
|
||||
throw new AccessDenyException();
|
||||
}
|
||||
})
|
||||
.then();
|
||||
if (id instanceof Publisher) {
|
||||
if (id instanceof Mono) {
|
||||
return ((Mono) id)
|
||||
.flatMap(r -> reactiveCheck.apply(r instanceof Collection ? ((Collection) r) : Collections.singleton(r)))
|
||||
.then((Mono) id);
|
||||
}
|
||||
if (id instanceof Flux) {
|
||||
return ((Flux) id)
|
||||
.collectList()
|
||||
.flatMap(reactiveCheck)
|
||||
.thenMany((Flux) id);
|
||||
}
|
||||
}
|
||||
Collection<Object> idVal = id instanceof Collection ? ((Collection) id) : Collections.singleton(id);
|
||||
|
||||
Object result = context.getParamContext().getInvokeResult();
|
||||
if (result instanceof Mono) {
|
||||
context.getParamContext()
|
||||
.setInvokeResult(((Mono) result)
|
||||
.flatMap(res -> {
|
||||
return reactiveCheck.apply(idVal).thenReturn(res);
|
||||
}));
|
||||
} else if (result instanceof Flux) {
|
||||
context.getParamContext()
|
||||
.setInvokeResult(((Flux) result)
|
||||
.flatMap(res -> {
|
||||
return reactiveCheck.apply(idVal).thenReturn(res);
|
||||
}));
|
||||
} else {
|
||||
// TODO: 2019-11-19 非响应式处理
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
protected boolean doHandleUpdate(DimensionDataAccessConfig cfg,
|
||||
DataAccessHandlerContext context) {
|
||||
MappingInfo info = getMappingInfo(context).get(cfg.getScopeType());
|
||||
if (info != null) {
|
||||
if (info.idParamIndex != -1) {
|
||||
Object param = context.getParamContext().getArguments()[info.idParamIndex];
|
||||
context.getParamContext().getArguments()[info.idParamIndex] = handleUpdateById(cfg, context, info, param);
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
|
||||
boolean reactive = context.getParamContext()
|
||||
.handleReactiveArguments(publisher -> {
|
||||
if (publisher instanceof Mono) {
|
||||
return Mono.from(publisher)
|
||||
.flatMap(payload -> applyReactiveUpdatePayload(cfg, info, Collections.singleton(payload), context)
|
||||
.thenReturn(payload));
|
||||
}
|
||||
if (publisher instanceof Flux) {
|
||||
return Flux.from(publisher)
|
||||
.collectList()
|
||||
.flatMapMany(list ->
|
||||
applyReactiveUpdatePayload(cfg, info, list, context)
|
||||
.flatMapIterable(v -> list));
|
||||
}
|
||||
|
||||
return publisher;
|
||||
});
|
||||
|
||||
if (!reactive) {
|
||||
applyUpdatePayload(cfg, info, Arrays
|
||||
.stream(context.getParamContext().getArguments())
|
||||
.flatMap(obj -> {
|
||||
if (obj instanceof Collection) {
|
||||
return ((Collection<?>) obj).stream();
|
||||
}
|
||||
return Stream.of(obj);
|
||||
})
|
||||
.filter(Entity.class::isInstance)
|
||||
.collect(Collectors.toSet()), context);
|
||||
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
protected void applyUpdatePayload(DimensionDataAccessConfig config,
|
||||
MappingInfo mappingInfo,
|
||||
Collection<?> payloads,
|
||||
DataAccessHandlerContext context) {
|
||||
List<Dimension> dimensions = context.getDimensions();
|
||||
|
||||
Set<Object> scope = CollectionUtils.isNotEmpty(config.getScope()) ?
|
||||
config.getScope() :
|
||||
dimensions
|
||||
.stream()
|
||||
.map(Dimension::getId)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
for (Object payload : payloads) {
|
||||
if (!(payload instanceof Entity)) {
|
||||
continue;
|
||||
}
|
||||
if (payload instanceof Param) {
|
||||
applyQueryParam(config, context, ((Param) payload));
|
||||
continue;
|
||||
}
|
||||
String property = mappingInfo.getProperty();
|
||||
Map<String, Object> map = FastBeanCopier.copy(payload, new HashMap<>(), FastBeanCopier.include(property));
|
||||
Object value = map.get(property);
|
||||
if (StringUtils.isEmpty(value)) {
|
||||
if (dimensions.size() == 1) {
|
||||
map.put(property, dimensions.get(0).getId());
|
||||
FastBeanCopier.copy(map, payload, property);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (CollectionUtils.isNotEmpty(scope)) {
|
||||
if (!scope.contains(value)) {
|
||||
throw new AccessDenyException();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected Mono<Void> applyReactiveUpdatePayload(DimensionDataAccessConfig config,
|
||||
MappingInfo info,
|
||||
Collection<?> payloads,
|
||||
DataAccessHandlerContext context) {
|
||||
|
||||
return Mono.fromRunnable(() -> applyUpdatePayload(config, info, payloads, context));
|
||||
}
|
||||
|
||||
protected boolean doHandleQuery(DimensionDataAccessConfig cfg, DataAccessHandlerContext requestContext) {
|
||||
boolean reactive = requestContext.getParamContext().handleReactiveArguments(publisher -> {
|
||||
if (publisher instanceof Mono) {
|
||||
return Mono
|
||||
.from(publisher)
|
||||
.flatMap(param -> this
|
||||
.applyReactiveQueryParam(cfg, requestContext, param)
|
||||
.thenReturn(param));
|
||||
}
|
||||
|
||||
return publisher;
|
||||
});
|
||||
|
||||
if (!reactive) {
|
||||
Object[] args = requestContext.getParamContext().getArguments();
|
||||
this.applyQueryParam(cfg, requestContext, args);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
protected String getTermType(DimensionDataAccessConfig cfg) {
|
||||
return "in";
|
||||
}
|
||||
|
||||
protected void applyQueryParam(DimensionDataAccessConfig cfg,
|
||||
DataAccessHandlerContext requestContext,
|
||||
Param param) {
|
||||
Set<Object> scope = CollectionUtils.isNotEmpty(cfg.getScope()) ?
|
||||
cfg.getScope() :
|
||||
requestContext.getDimensions()
|
||||
.stream()
|
||||
.map(Dimension::getId)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
QueryParamEntity entity = new QueryParamEntity();
|
||||
entity.setTerms(new ArrayList<>(param.getTerms()));
|
||||
entity.toNestQuery(query ->
|
||||
query.where(
|
||||
getProperty(cfg, requestContext),
|
||||
getTermType(cfg),
|
||||
scope));
|
||||
param.setTerms(entity.getTerms());
|
||||
}
|
||||
|
||||
protected void applyQueryParam(DimensionDataAccessConfig cfg,
|
||||
DataAccessHandlerContext requestContext,
|
||||
Object... params) {
|
||||
for (Object param : params) {
|
||||
if (param instanceof QueryParam) {
|
||||
applyQueryParam(cfg, requestContext, (QueryParam) param);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected Mono<Void> applyReactiveQueryParam(DimensionDataAccessConfig cfg,
|
||||
DataAccessHandlerContext requestContext,
|
||||
Object... param) {
|
||||
|
||||
|
||||
return Mono.fromRunnable(() -> applyQueryParam(cfg, requestContext, param));
|
||||
}
|
||||
|
||||
private Map<Method, Map<String, MappingInfo>> cache = new ConcurrentHashMap<>();
|
||||
|
||||
|
||||
public Map<String, MappingInfo> getMappingInfo(DataAccessHandlerContext context) {
|
||||
return getMappingInfo(ClassUtils.getUserClass(context.getParamContext().getTarget()), context.getParamContext().getMethod());
|
||||
|
||||
}
|
||||
|
||||
private Set<Class<? extends Annotation>> ann = new HashSet<>(Arrays.asList(DimensionDataAccess.class, DimensionDataAccess.Mapping.class));
|
||||
|
||||
|
||||
private Map<String, MappingInfo> getMappingInfo(Class target, Method method) {
|
||||
|
||||
return cache.computeIfAbsent(method, m -> {
|
||||
Set<Annotation> methodAnnotation = AnnotatedElementUtils.findAllMergedAnnotations(method, ann);
|
||||
Set<Annotation> classAnnotation = AnnotatedElementUtils.findAllMergedAnnotations(target, ann);
|
||||
|
||||
if (CollectionUtils.isEmpty(methodAnnotation)) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
List<Annotation> all = new ArrayList<>(classAnnotation);
|
||||
all.addAll(methodAnnotation);
|
||||
|
||||
Map<String, MappingInfo> mappingInfoMap = new HashMap<>();
|
||||
for (Annotation annotation : all) {
|
||||
if (annotation instanceof DimensionDataAccess) {
|
||||
for (DimensionDataAccess.Mapping mapping : ((DimensionDataAccess) annotation).mapping()) {
|
||||
mappingInfoMap.put(mapping.dimensionType(), MappingInfo.of(mapping));
|
||||
}
|
||||
}
|
||||
if (annotation instanceof DimensionDataAccess.Mapping) {
|
||||
mappingInfoMap.put(((DimensionDataAccess.Mapping) annotation).dimensionType(), MappingInfo.of(((DimensionDataAccess.Mapping) annotation)));
|
||||
}
|
||||
}
|
||||
return mappingInfoMap;
|
||||
});
|
||||
}
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@AllArgsConstructor
|
||||
static class MappingInfo {
|
||||
String dimension;
|
||||
|
||||
String property;
|
||||
|
||||
int idParamIndex;
|
||||
|
||||
static MappingInfo of(DimensionDataAccess.Mapping mapping) {
|
||||
return new MappingInfo(mapping.dimensionType(), mapping.property(), mapping.idParamIndex());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -39,6 +39,7 @@ public class FieldFilterDataAccessHandler implements DataAccessHandler {
|
||||
case Permission.ACTION_GET:
|
||||
return doQueryAccess(filterDataAccessConfig, context);
|
||||
case Permission.ACTION_ADD:
|
||||
case Permission.ACTION_SAVE:
|
||||
case Permission.ACTION_UPDATE:
|
||||
return doUpdateAccess(filterDataAccessConfig, context);
|
||||
default:
|
||||
|
||||
@@ -2,36 +2,20 @@ package org.hswebframework.web.authorization.basic.aop;
|
||||
|
||||
import org.hswebframework.ezorm.core.param.Param;
|
||||
import org.hswebframework.ezorm.core.param.QueryParam;
|
||||
import org.hswebframework.web.authorization.Authentication;
|
||||
import org.hswebframework.web.authorization.ReactiveAuthenticationHolder;
|
||||
import org.hswebframework.web.authorization.ReactiveAuthenticationSupplier;
|
||||
import org.hswebframework.web.authorization.User;
|
||||
import org.hswebframework.web.authorization.basic.handler.access.FieldFilterDataAccessHandler;
|
||||
import org.hswebframework.web.authorization.basic.web.ReactiveUserTokenParser;
|
||||
import org.hswebframework.ezorm.core.param.Term;
|
||||
import org.hswebframework.web.authorization.*;
|
||||
import org.hswebframework.web.authorization.exception.AccessDenyException;
|
||||
import org.hswebframework.web.authorization.exception.UnAuthorizedException;
|
||||
import org.hswebframework.web.authorization.simple.SimpleAuthentication;
|
||||
import org.hswebframework.web.authorization.simple.SimpleFieldFilterDataAccessConfig;
|
||||
import org.hswebframework.web.authorization.simple.SimplePermission;
|
||||
import org.hswebframework.web.authorization.simple.SimpleUser;
|
||||
import org.hswebframework.web.authorization.token.ParsedToken;
|
||||
import org.junit.Before;
|
||||
import org.hswebframework.web.authorization.simple.*;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import java.util.*;
|
||||
import java.util.function.Function;
|
||||
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@SpringBootTest(classes = TestApplication.class)
|
||||
@@ -76,10 +60,10 @@ public class AopAuthorizingControllerTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFiledDeny(){
|
||||
public void testFiledDeny() {
|
||||
SimpleAuthentication authentication = new SimpleAuthentication();
|
||||
|
||||
SimpleFieldFilterDataAccessConfig config=new SimpleFieldFilterDataAccessConfig();
|
||||
SimpleFieldFilterDataAccessConfig config = new SimpleFieldFilterDataAccessConfig();
|
||||
config.setAction("query");
|
||||
config.setFields(new HashSet<>(Arrays.asList("name")));
|
||||
|
||||
@@ -104,13 +88,84 @@ public class AopAuthorizingControllerTest {
|
||||
testController.queryUser(new QueryParam())
|
||||
.map(Param::getExcludes)
|
||||
.as(StepVerifier::create)
|
||||
.expectNextMatches(f->f.contains("name"))
|
||||
.expectNextMatches(f -> f.contains("name"))
|
||||
.verifyComplete();
|
||||
|
||||
testController.queryUser(Mono.just(new QueryParam()))
|
||||
.map(Param::getExcludes)
|
||||
.as(StepVerifier::create)
|
||||
.expectNextMatches(f->f.contains("name"))
|
||||
.expectNextMatches(f -> f.contains("name"))
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDimensionDataAccess() {
|
||||
SimpleAuthentication authentication = new SimpleAuthentication();
|
||||
|
||||
DimensionDataAccessConfig config = new DimensionDataAccessConfig();
|
||||
config.setAction("query");
|
||||
config.setScopeType("role");
|
||||
|
||||
DimensionDataAccessConfig config2 = new DimensionDataAccessConfig();
|
||||
config2.setAction("save");
|
||||
config2.setScopeType("role");
|
||||
ReactiveAuthenticationHolder.addSupplier(new ReactiveAuthenticationSupplier() {
|
||||
@Override
|
||||
public Mono<Authentication> get(String userId) {
|
||||
return Mono.empty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Authentication> get() {
|
||||
return Mono.just(authentication);
|
||||
}
|
||||
});
|
||||
|
||||
authentication.setUser(SimpleUser.builder().id("test").username("test").build());
|
||||
authentication.setPermissions(Arrays.asList(SimplePermission.builder()
|
||||
.actions(new HashSet<>(Arrays.asList("query", "save")))
|
||||
.dataAccesses(new HashSet<>(Arrays.asList(config, config2)))
|
||||
.id("test").build()));
|
||||
authentication.setDimensions(Collections.singletonList(Dimension.of("test", "test", DefaultDimensionType.role)));
|
||||
|
||||
testController.queryUserByDimension(Mono.just(new QueryParam()))
|
||||
.map(Param::getTerms)
|
||||
.flatMapIterable(Function.identity())
|
||||
.next()
|
||||
.map(Term::getValue)
|
||||
.<Collection<Object>>map(Collection.class::cast)
|
||||
.flatMapIterable(Function.identity())
|
||||
.next()
|
||||
.as(StepVerifier::create)
|
||||
.expectNextMatches("test"::equals)
|
||||
.verifyComplete();
|
||||
|
||||
TestEntity testEntity = new TestEntity();
|
||||
testEntity.setRoleId("123");
|
||||
|
||||
testController.save(Mono.just(testEntity))
|
||||
.as(StepVerifier::create)
|
||||
.expectError(AccessDenyException.class)
|
||||
.verify();
|
||||
|
||||
testController.add(Mono.just(testEntity))
|
||||
.as(StepVerifier::create)
|
||||
.expectNextCount(1)
|
||||
.verifyComplete();
|
||||
|
||||
testController.update(testEntity.getId(),Mono.just(testEntity))
|
||||
.as(StepVerifier::create)
|
||||
.expectError(AccessDenyException.class)
|
||||
.verify();
|
||||
|
||||
testEntity = new TestEntity();
|
||||
testEntity.setRoleId("test");
|
||||
|
||||
testController.save(Mono.just(testEntity))
|
||||
.as(StepVerifier::create)
|
||||
.expectNextCount(1)
|
||||
.verifyComplete();
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,13 @@
|
||||
package org.hswebframework.web.authorization.basic.aop;
|
||||
|
||||
import org.hswebframework.web.authorization.basic.configuration.EnableAopAuthorize;
|
||||
import org.hswebframework.web.crud.annotation.EnableEasyormRepository;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
@SpringBootApplication
|
||||
@EnableAopAuthorize
|
||||
@EnableEasyormRepository("org.hswebframework.web.authorization.basic.aop")
|
||||
public class TestApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
||||
@@ -1,18 +1,21 @@
|
||||
package org.hswebframework.web.authorization.basic.aop;
|
||||
|
||||
import org.hswebframework.ezorm.core.param.QueryParam;
|
||||
import org.hswebframework.ezorm.rdb.mapping.ReactiveRepository;
|
||||
import org.hswebframework.web.authorization.Authentication;
|
||||
import org.hswebframework.web.authorization.User;
|
||||
import org.hswebframework.web.authorization.access.DataAccessConfig;
|
||||
import org.hswebframework.web.authorization.annotation.*;
|
||||
import org.hswebframework.web.authorization.define.Phased;
|
||||
import org.hswebframework.web.authorization.exception.UnAuthorizedException;
|
||||
import org.hswebframework.web.crud.web.reactive.ReactiveCrudController;
|
||||
import org.hswebframework.web.crud.web.reactive.ReactiveQueryController;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
@RestController
|
||||
@Resource(id = "test", name = "测试")
|
||||
public class TestController {
|
||||
public class TestController implements ReactiveCrudController<TestEntity, String> {
|
||||
|
||||
@QueryAction
|
||||
public Mono<User> getUser() {
|
||||
@@ -28,15 +31,41 @@ public class TestController {
|
||||
.map(Authentication::getUser);
|
||||
}
|
||||
|
||||
@QueryAction(dataAccess = @DataAccess(type = @DataAccessType(id= DataAccessConfig.DefaultType.DENY_FIELDS,name = "禁止访问字段")))
|
||||
@QueryAction
|
||||
@FieldDataAccess
|
||||
public Mono<QueryParam> queryUser(QueryParam queryParam) {
|
||||
return Mono.just(queryParam);
|
||||
}
|
||||
|
||||
@QueryAction(dataAccess = @DataAccess(type = @DataAccessType(id= DataAccessConfig.DefaultType.DENY_FIELDS,name = "禁止访问字段")))
|
||||
@QueryAction
|
||||
@FieldDataAccess
|
||||
public Mono<QueryParam> queryUser(Mono<QueryParam> queryParam) {
|
||||
return queryParam;
|
||||
}
|
||||
|
||||
@QueryAction
|
||||
@TestDataAccess
|
||||
public Mono<QueryParam> queryUserByDimension(Mono<QueryParam> queryParam) {
|
||||
return queryParam;
|
||||
}
|
||||
|
||||
@SaveAction
|
||||
@TestDataAccess
|
||||
public Mono<TestEntity> save(Mono<TestEntity> param) {
|
||||
return param;
|
||||
}
|
||||
|
||||
@Override
|
||||
@TestDataAccess(idParamIndex = 0,phased = Phased.after)
|
||||
public Mono<Boolean> update(String id, Mono<TestEntity> payload) {
|
||||
return ReactiveCrudController.super.update(id, payload);
|
||||
}
|
||||
|
||||
@Autowired
|
||||
ReactiveRepository<TestEntity, String> reactiveRepository;
|
||||
|
||||
@Override
|
||||
public ReactiveRepository<TestEntity, String> getRepository() {
|
||||
return reactiveRepository;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
package org.hswebframework.web.authorization.basic.aop;
|
||||
|
||||
import org.hswebframework.web.authorization.annotation.DimensionDataAccess;
|
||||
import org.hswebframework.web.authorization.define.Phased;
|
||||
import org.springframework.core.annotation.AliasFor;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@Target({ElementType.ANNOTATION_TYPE, ElementType.METHOD})
|
||||
@DimensionDataAccess
|
||||
@DimensionDataAccess.Mapping(dimensionType = "role", property = "roleId")
|
||||
public @interface TestDataAccess {
|
||||
|
||||
@AliasFor(annotation = DimensionDataAccess.Mapping.class)
|
||||
int idParamIndex() default -1;
|
||||
|
||||
@AliasFor(annotation = DimensionDataAccess.class)
|
||||
Phased phased() default Phased.before;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package org.hswebframework.web.authorization.basic.aop;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.hswebframework.web.api.crud.entity.GenericEntity;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Table;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@Table(name = "test_entity")
|
||||
public class TestEntity extends GenericEntity<String> {
|
||||
|
||||
@Column
|
||||
private String roleId;
|
||||
|
||||
|
||||
}
|
||||
@@ -7,4 +7,9 @@ hsweb:
|
||||
permissions-simple:
|
||||
user-token:
|
||||
- get
|
||||
- update
|
||||
- update
|
||||
easyorm:
|
||||
dialect: h2
|
||||
logging:
|
||||
level:
|
||||
org.hswebframework: debug
|
||||
Reference in New Issue
Block a user