mirror of
https://github.com/hs-web/hsweb-framework.git
synced 2026-05-22 17:07:12 +08:00
feat: 适配spring-boot3
This commit is contained in:
@@ -3,33 +3,20 @@ package org.hswebframework.web.authorization.basic.configuration;
|
||||
import org.hswebframework.web.authorization.AuthenticationManager;
|
||||
import org.hswebframework.web.authorization.ReactiveAuthenticationManagerProvider;
|
||||
import org.hswebframework.web.authorization.access.DataAccessController;
|
||||
import org.hswebframework.web.authorization.access.DataAccessHandler;
|
||||
import org.hswebframework.web.authorization.basic.aop.AopMethodAuthorizeDefinitionParser;
|
||||
import org.hswebframework.web.authorization.basic.embed.EmbedAuthenticationProperties;
|
||||
import org.hswebframework.web.authorization.basic.embed.EmbedReactiveAuthenticationManager;
|
||||
import org.hswebframework.web.authorization.basic.handler.DefaultAuthorizingHandler;
|
||||
import org.hswebframework.web.authorization.basic.handler.UserAllowPermissionHandler;
|
||||
import org.hswebframework.web.authorization.basic.handler.access.DefaultDataAccessController;
|
||||
import org.hswebframework.web.authorization.basic.twofactor.TwoFactorHandlerInterceptorAdapter;
|
||||
import org.hswebframework.web.authorization.basic.web.*;
|
||||
import org.hswebframework.web.authorization.token.UserTokenManager;
|
||||
import org.hswebframework.web.authorization.twofactor.TwoFactorValidatorManager;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.config.BeanPostProcessor;
|
||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.condition.*;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 权限控制自动配置类
|
||||
@@ -41,14 +28,10 @@ import java.util.List;
|
||||
@EnableConfigurationProperties(EmbedAuthenticationProperties.class)
|
||||
public class AuthorizingHandlerAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
public DefaultDataAccessController dataAccessController() {
|
||||
return new DefaultDataAccessController();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public DefaultAuthorizingHandler authorizingHandler(DataAccessController dataAccessController) {
|
||||
return new DefaultAuthorizingHandler(dataAccessController);
|
||||
public DefaultAuthorizingHandler authorizingHandler() {
|
||||
return new DefaultAuthorizingHandler(null);
|
||||
}
|
||||
|
||||
|
||||
@@ -93,27 +76,6 @@ public class AuthorizingHandlerAutoConfiguration {
|
||||
return new BearerTokenParser();
|
||||
}
|
||||
|
||||
@Configuration
|
||||
public static class DataAccessHandlerProcessor implements BeanPostProcessor {
|
||||
|
||||
@Autowired
|
||||
private DefaultDataAccessController defaultDataAccessController;
|
||||
|
||||
@Override
|
||||
public Object postProcessBeforeInitialization(Object bean, String beanName) {
|
||||
return bean;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object postProcessAfterInitialization(Object bean, String beanName) {
|
||||
if (bean instanceof DataAccessHandler) {
|
||||
defaultDataAccessController.addHandler(((DataAccessHandler) bean));
|
||||
}
|
||||
return bean;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnProperty(prefix = "hsweb.authorize", name = "basic-authorization", havingValue = "true")
|
||||
@ConditionalOnClass(UserTokenForTypeParser.class)
|
||||
|
||||
@@ -11,7 +11,7 @@ import org.hswebframework.web.authorization.token.UserToken;
|
||||
import org.hswebframework.web.authorization.token.UserTokenManager;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
|
||||
public class BasicAuthorizationTokenParser implements UserTokenForTypeParser {
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package org.hswebframework.web.authorization.basic.configuration;
|
||||
|
||||
import org.hswebframework.web.authorization.basic.aop.AopMethodAuthorizeDefinitionParser;
|
||||
import org.hswebframework.web.authorization.basic.twofactor.TwoFactorHandlerInterceptorAdapter;
|
||||
import org.hswebframework.web.authorization.basic.web.*;
|
||||
import org.hswebframework.web.authorization.token.UserTokenManager;
|
||||
import org.hswebframework.web.authorization.twofactor.TwoFactorValidatorManager;
|
||||
@@ -15,7 +14,7 @@ import org.springframework.core.annotation.Order;
|
||||
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import jakarta.annotation.Nonnull;
|
||||
import java.util.List;
|
||||
|
||||
@AutoConfiguration
|
||||
@@ -64,16 +63,4 @@ public class WebMvcAuthorizingConfiguration {
|
||||
return new SessionIdUserTokenGenerator();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnProperty(prefix = "hsweb.authorize.two-factor", name = "enable", havingValue = "true")
|
||||
@Order(100)
|
||||
public WebMvcConfigurer twoFactorHandlerConfigurer(TwoFactorValidatorManager manager) {
|
||||
return new WebMvcConfigurer() {
|
||||
@Override
|
||||
public void addInterceptors(@Nonnull InterceptorRegistry registry) {
|
||||
registry.addInterceptor(new TwoFactorHandlerInterceptorAdapter(manager));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
@@ -11,10 +11,8 @@ import org.hswebframework.web.authorization.simple.builder.SimpleDataAccessConfi
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.util.StringUtils;
|
||||
import reactor.core.publisher.Mono;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
import javax.validation.ValidationException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
@@ -36,10 +34,6 @@ import java.util.Optional;
|
||||
* permissions:
|
||||
* - id: user-manager
|
||||
* actions: *
|
||||
* dataAccesses:
|
||||
* - action: query
|
||||
* type: DENY_FIELDS
|
||||
* fields: password,salt
|
||||
* </pre>
|
||||
*
|
||||
* @author zhouhao
|
||||
@@ -62,25 +56,9 @@ public class EmbedAuthenticationProperties implements InitializingBean {
|
||||
@Override
|
||||
public void afterPropertiesSet() {
|
||||
users.forEach((id, properties) -> {
|
||||
if (StringUtils.isEmpty(properties.getId())) {
|
||||
if (ObjectUtils.isEmpty(properties.getId())) {
|
||||
properties.setId(id);
|
||||
}
|
||||
for (EmbedAuthenticationInfo.PermissionInfo permissionInfo : properties.getPermissions()) {
|
||||
for (Map<String, Object> objectMap : permissionInfo.getDataAccesses()) {
|
||||
for (Map.Entry<String, Object> stringObjectEntry : objectMap.entrySet()) {
|
||||
if (stringObjectEntry.getValue() instanceof Map) {
|
||||
Map<?, ?> mapVal = ((Map) stringObjectEntry.getValue());
|
||||
boolean maybeIsList = mapVal
|
||||
.keySet()
|
||||
.stream()
|
||||
.allMatch(org.hswebframework.utils.StringUtils::isInt);
|
||||
if (maybeIsList) {
|
||||
stringObjectEntry.setValue(mapVal.values());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
authentications.put(id, properties.toAuthentication(dataAccessConfigBuilderFactory));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -69,7 +69,6 @@ public class DefaultAuthorizingHandler implements AuthorizingHandler {
|
||||
public void handleDataAccess(AuthorizingContext context) {
|
||||
|
||||
if (dataAccessController == null) {
|
||||
log.warn("dataAccessController is null,skip result access control!");
|
||||
return;
|
||||
}
|
||||
if (context.getDefinition().getResources() == null) {
|
||||
|
||||
@@ -1,63 +0,0 @@
|
||||
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.AuthorizeDefinition;
|
||||
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;
|
||||
|
||||
private AuthorizeDefinition definition;
|
||||
|
||||
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());
|
||||
requestContext.setDefinition(context.getDefinition());
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
package org.hswebframework.web.authorization.basic.handler.access;
|
||||
|
||||
import org.hswebframework.web.authorization.access.DataAccessConfig;
|
||||
import org.hswebframework.web.authorization.access.DataAccessController;
|
||||
import org.hswebframework.web.authorization.access.DataAccessHandler;
|
||||
import org.hswebframework.web.authorization.define.AuthorizingContext;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 默认的行级权限控制.通过获取DataAccessHandler进行实际处理
|
||||
*
|
||||
* @author zhouhao
|
||||
* @see DataAccessHandler
|
||||
* @since 3.0
|
||||
*/
|
||||
public final class DefaultDataAccessController implements DataAccessController {
|
||||
|
||||
private DataAccessController parent;
|
||||
|
||||
private List<DataAccessHandler> handlers = new LinkedList<>();
|
||||
|
||||
public DefaultDataAccessController() {
|
||||
this(null);
|
||||
}
|
||||
|
||||
public DefaultDataAccessController(DataAccessController parent) {
|
||||
if (parent == this) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
this.parent = parent;
|
||||
addHandler(new FieldFilterDataAccessHandler())
|
||||
.addHandler(new DimensionDataAccessHandler());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean doAccess(DataAccessConfig access, AuthorizingContext context) {
|
||||
if (parent != null) {
|
||||
parent.doAccess(access, context);
|
||||
}
|
||||
return handlers.stream()
|
||||
.filter(handler -> handler.isSupport(access))
|
||||
.allMatch(handler -> handler.handle(access, context));
|
||||
}
|
||||
|
||||
public DefaultDataAccessController addHandler(DataAccessHandler handler) {
|
||||
handlers.add(handler);
|
||||
return this;
|
||||
}
|
||||
|
||||
public void setHandlers(List<DataAccessHandler> handlers) {
|
||||
this.handlers = handlers;
|
||||
}
|
||||
|
||||
public List<DataAccessHandler> getHandlers() {
|
||||
return handlers;
|
||||
}
|
||||
}
|
||||
@@ -1,440 +0,0 @@
|
||||
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.define.Phased;
|
||||
import org.hswebframework.web.authorization.exception.AccessDenyException;
|
||||
import org.hswebframework.web.authorization.simple.DimensionDataAccessConfig;
|
||||
import org.hswebframework.web.bean.FastBeanCopier;
|
||||
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 handleById(DimensionDataAccessConfig config,
|
||||
DataAccessHandlerContext context,
|
||||
MappingInfo mappingInfo,
|
||||
Object id) {
|
||||
|
||||
if (id instanceof Param || id instanceof Entity) {
|
||||
|
||||
applyQueryParam(config, context, id);
|
||||
return 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 -> {
|
||||
if (r instanceof Param) {
|
||||
applyQueryParam(config, context, r);
|
||||
return Mono.just(r);
|
||||
}
|
||||
return reactiveCheck.apply(r instanceof Collection ? ((Collection) r) : Collections.singleton(r));
|
||||
|
||||
})
|
||||
.then((Mono) id);
|
||||
}
|
||||
if (id instanceof Flux) {
|
||||
return ((Flux) id)
|
||||
.filter(v -> {
|
||||
if (v instanceof Param) {
|
||||
applyQueryParam(config, context, v);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
})
|
||||
.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(reactiveCheck.apply(idVal).then(((Mono) result)));
|
||||
|
||||
} else if (result instanceof Flux) {
|
||||
context.getParamContext()
|
||||
.setInvokeResult(reactiveCheck.apply(idVal).thenMany(((Flux) result)));
|
||||
} else {
|
||||
// TODO: 2019-11-19 非响应式处理
|
||||
log.warn("unsupported handle data access by id :{}", context.getParamContext().getMethod());
|
||||
}
|
||||
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] = handleById(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 hasAccessByProperty(Set<Object> scope, String property, Object payload) {
|
||||
Map<String, Object> values = FastBeanCopier.copy(payload, new HashMap<>(), FastBeanCopier.include(property));
|
||||
Object val = values.get(property);
|
||||
return val == null || scope.contains(val);
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
protected boolean doHandleQuery(DimensionDataAccessConfig cfg, DataAccessHandlerContext context) {
|
||||
MappingInfo mappingInfo = getMappingInfo(context).get(cfg.getScopeType());
|
||||
|
||||
//根据结果控制
|
||||
if (context.getDefinition().getResources().getPhased() == Phased.after) {
|
||||
Object result = context.getParamContext().getInvokeResult();
|
||||
Set<Object> scope = CollectionUtils.isNotEmpty(cfg.getScope()) ?
|
||||
cfg.getScope() :
|
||||
context.getDimensions()
|
||||
.stream()
|
||||
.map(Dimension::getId)
|
||||
.collect(Collectors.toSet());
|
||||
String property = mappingInfo.getProperty();
|
||||
|
||||
if (result instanceof Mono) {
|
||||
context.getParamContext()
|
||||
.setInvokeResult(((Mono) result).
|
||||
filter(data -> hasAccessByProperty(scope, property, data)));
|
||||
return true;
|
||||
} else if (result instanceof Flux) {
|
||||
context.getParamContext()
|
||||
.setInvokeResult(((Flux) result).
|
||||
filter(data -> hasAccessByProperty(scope, property, data)));
|
||||
return true;
|
||||
}
|
||||
return hasAccessByProperty(scope, property, result);
|
||||
}
|
||||
//根据id控制
|
||||
if (mappingInfo.getIdParamIndex() >= 0) {
|
||||
Object param = context.getParamContext().getArguments()[mappingInfo.idParamIndex];
|
||||
context.getParamContext().getArguments()[mappingInfo.idParamIndex] = handleById(cfg, context, mappingInfo, param);
|
||||
return true;
|
||||
}
|
||||
|
||||
//根据查询条件控制
|
||||
boolean reactive = context.getParamContext().handleReactiveArguments(publisher -> {
|
||||
if (publisher instanceof Mono) {
|
||||
return Mono
|
||||
.from(publisher)
|
||||
.flatMap(param -> this
|
||||
.applyReactiveQueryParam(cfg, context, param)
|
||||
.thenReturn(param));
|
||||
}
|
||||
|
||||
return publisher;
|
||||
});
|
||||
|
||||
if (!reactive) {
|
||||
Object[] args = context.getParamContext().getArguments();
|
||||
this.applyQueryParam(cfg, context, 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);
|
||||
|
||||
|
||||
List<Annotation> all = new ArrayList<>(classAnnotation);
|
||||
all.addAll(methodAnnotation);
|
||||
if (CollectionUtils.isEmpty(all)) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
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());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,167 +0,0 @@
|
||||
package org.hswebframework.web.authorization.basic.handler.access;
|
||||
|
||||
import org.apache.commons.beanutils.BeanUtilsBean;
|
||||
import org.hswebframework.ezorm.core.param.QueryParam;
|
||||
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.access.FieldFilterDataAccessConfig;
|
||||
import org.hswebframework.web.authorization.define.AuthorizingContext;
|
||||
import org.hswebframework.web.authorization.define.Phased;
|
||||
import org.reactivestreams.Publisher;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* 数据权限字段过滤处理,目前仅支持deny. {@link DataAccessConfig.DefaultType#DENY_FIELDS}
|
||||
*
|
||||
* @author zhouhao
|
||||
*/
|
||||
public class FieldFilterDataAccessHandler implements DataAccessHandler {
|
||||
private Logger logger = LoggerFactory.getLogger(this.getClass());
|
||||
|
||||
@Override
|
||||
public boolean isSupport(DataAccessConfig access) {
|
||||
return access instanceof FieldFilterDataAccessConfig;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean handle(DataAccessConfig access, AuthorizingContext context) {
|
||||
FieldFilterDataAccessConfig filterDataAccessConfig = ((FieldFilterDataAccessConfig) access);
|
||||
|
||||
switch (access.getAction()) {
|
||||
case Permission.ACTION_QUERY:
|
||||
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:
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("field filter not support for {}", access.getAction());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
protected void applyUpdateParam(FieldFilterDataAccessConfig config, Object... parameter) {
|
||||
|
||||
for (Object data : parameter) {
|
||||
for (String field : config.getFields()) {
|
||||
try {
|
||||
//设置值为null,跳过修改
|
||||
BeanUtilsBean.getInstance()
|
||||
.getPropertyUtils()
|
||||
.setProperty(data, field, null);
|
||||
} catch (Exception e) {
|
||||
logger.warn("can't set {} null", field, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param accesses 不可操作的字段
|
||||
* @param params 参数上下文
|
||||
* @return true
|
||||
* @see BeanUtilsBean
|
||||
* @see org.apache.commons.beanutils.PropertyUtilsBean
|
||||
*/
|
||||
protected boolean doUpdateAccess(FieldFilterDataAccessConfig accesses, AuthorizingContext params) {
|
||||
|
||||
boolean reactive = params.getParamContext().handleReactiveArguments(publisher -> {
|
||||
if (publisher instanceof Mono) {
|
||||
return Mono.from(publisher)
|
||||
.doOnNext(data -> applyUpdateParam(accesses, data));
|
||||
|
||||
}
|
||||
if (publisher instanceof Flux) {
|
||||
return Flux.from(publisher)
|
||||
.doOnNext(data -> applyUpdateParam(accesses, data));
|
||||
|
||||
}
|
||||
return publisher;
|
||||
});
|
||||
if (reactive) {
|
||||
return true;
|
||||
}
|
||||
|
||||
applyUpdateParam(accesses, params.getParamContext().getArguments());
|
||||
return true;
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
protected void applyQueryParam(FieldFilterDataAccessConfig config, Object param) {
|
||||
if (param instanceof QueryParam) {
|
||||
Set<String> denyFields = config.getFields();
|
||||
((QueryParam) param).excludes(denyFields.toArray(new String[0]));
|
||||
return;
|
||||
}
|
||||
|
||||
Object r = InvokeResultUtils.convertRealResult(param);
|
||||
if (r instanceof Collection) {
|
||||
((Collection) r).forEach(o -> setObjectPropertyNull(o, config.getFields()));
|
||||
} else {
|
||||
setObjectPropertyNull(r, config.getFields());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
protected boolean doQueryAccess(FieldFilterDataAccessConfig access, AuthorizingContext context) {
|
||||
if (context.getDefinition().getResources().getPhased() == Phased.before) {
|
||||
|
||||
boolean reactive = context
|
||||
.getParamContext()
|
||||
.handleReactiveArguments(publisher -> {
|
||||
if (publisher instanceof Mono) {
|
||||
return Mono.from(publisher)
|
||||
.doOnNext(param -> {
|
||||
applyQueryParam(access, param);
|
||||
});
|
||||
}
|
||||
return publisher;
|
||||
});
|
||||
|
||||
if (reactive) {
|
||||
return true;
|
||||
}
|
||||
|
||||
for (Object argument : context.getParamContext().getArguments()) {
|
||||
applyQueryParam(access, argument);
|
||||
}
|
||||
} else {
|
||||
if (context.getParamContext().getInvokeResult() instanceof Publisher) {
|
||||
context.getParamContext().setInvokeResult(
|
||||
Flux.from((Publisher<?>) context.getParamContext().getInvokeResult())
|
||||
.doOnNext(result -> {
|
||||
applyQueryParam(access, result);
|
||||
})
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
applyQueryParam(access, context.getParamContext().getInvokeResult());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
protected void setObjectPropertyNull(Object obj, Set<String> fields) {
|
||||
if (null == obj) {
|
||||
return;
|
||||
}
|
||||
for (String field : fields) {
|
||||
try {
|
||||
BeanUtilsBean.getInstance().getPropertyUtils().setProperty(obj, field, null);
|
||||
} catch (Exception ignore) {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
package org.hswebframework.web.authorization.basic.handler.access;
|
||||
|
||||
import org.springframework.http.ResponseEntity;
|
||||
|
||||
public class InvokeResultUtils {
|
||||
public static Object convertRealResult(Object result) {
|
||||
if (result instanceof ResponseEntity) {
|
||||
result = ((ResponseEntity) result).getBody();
|
||||
}
|
||||
// if (result instanceof ResponseMessage) {
|
||||
// result = ((ResponseMessage) result).getResult();
|
||||
// }
|
||||
// if (result instanceof PagerResult) {
|
||||
// result = ((PagerResult) result).getData();
|
||||
// }
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
package org.hswebframework.web.authorization.basic.twofactor;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.hswebframework.web.authorization.Authentication;
|
||||
import org.hswebframework.web.authorization.User;
|
||||
import org.hswebframework.web.authorization.annotation.TwoFactor;
|
||||
import org.hswebframework.web.authorization.exception.NeedTwoFactorException;
|
||||
import org.hswebframework.web.authorization.twofactor.TwoFactorValidator;
|
||||
import org.hswebframework.web.authorization.twofactor.TwoFactorValidatorManager;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.method.HandlerMethod;
|
||||
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
/**
|
||||
* @author zhouhao
|
||||
* @since 3.0.4
|
||||
*/
|
||||
@AllArgsConstructor
|
||||
public class TwoFactorHandlerInterceptorAdapter extends HandlerInterceptorAdapter {
|
||||
|
||||
private final TwoFactorValidatorManager validatorManager;
|
||||
|
||||
@Override
|
||||
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
|
||||
if (handler instanceof HandlerMethod) {
|
||||
HandlerMethod method = ((HandlerMethod) handler);
|
||||
TwoFactor factor = method.getMethodAnnotation(TwoFactor.class);
|
||||
if (factor == null || factor.ignore()) {
|
||||
return true;
|
||||
}
|
||||
String userId = Authentication.current()
|
||||
.map(Authentication::getUser)
|
||||
.map(User::getId)
|
||||
.orElse(null);
|
||||
TwoFactorValidator validator = validatorManager.getValidator(userId, factor.value(), factor.provider());
|
||||
if (!validator.expired()) {
|
||||
return true;
|
||||
}
|
||||
String code = request.getParameter(factor.parameter());
|
||||
if (code == null) {
|
||||
code = request.getHeader(factor.parameter());
|
||||
}
|
||||
if (StringUtils.isEmpty(code)) {
|
||||
throw new NeedTwoFactorException("validation.need_two_factor_verify", factor.provider());
|
||||
} else if (!validator.verify(code, factor.timeout())) {
|
||||
throw new NeedTwoFactorException(factor.message(), factor.provider());
|
||||
}
|
||||
}
|
||||
return super.preHandle(request, response, handler);
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,7 @@ import org.hswebframework.web.authorization.token.ParsedToken;
|
||||
import org.hswebframework.web.id.IDGenerator;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
@@ -3,7 +3,7 @@ package org.hswebframework.web.authorization.basic.web;
|
||||
import org.hswebframework.web.authorization.Authentication;
|
||||
import org.hswebframework.web.utils.WebUtils;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import java.io.Serializable;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
@@ -5,8 +5,8 @@ import org.hswebframework.web.authorization.token.UserToken;
|
||||
import org.hswebframework.web.authorization.token.UserTokenManager;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpSession;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpSession;
|
||||
|
||||
import static org.hswebframework.web.authorization.basic.web.UserTokenGenerator.TOKEN_TYPE_SESSION_ID;
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ package org.hswebframework.web.authorization.basic.web;
|
||||
|
||||
import org.hswebframework.web.authorization.token.ParsedToken;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
|
||||
/**
|
||||
* 令牌解析器,用于在接受到请求到时候,从请求中获取令牌
|
||||
|
||||
@@ -7,10 +7,11 @@ import org.hswebframework.web.authorization.token.UserToken;
|
||||
import org.hswebframework.web.authorization.token.UserTokenHolder;
|
||||
import org.hswebframework.web.authorization.token.UserTokenManager;
|
||||
import org.springframework.web.method.HandlerMethod;
|
||||
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.springframework.web.servlet.HandlerInterceptor;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
@@ -20,7 +21,7 @@ import java.util.stream.Collectors;
|
||||
*
|
||||
* @author zhouhao
|
||||
*/
|
||||
public class WebUserTokenInterceptor extends HandlerInterceptorAdapter {
|
||||
public class WebUserTokenInterceptor implements HandlerInterceptor {
|
||||
|
||||
private final UserTokenManager userTokenManager;
|
||||
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
module hsweb.authorization.basic {
|
||||
requires spring.core;
|
||||
requires hsweb.core;
|
||||
requires hsweb.authorization.api;
|
||||
requires hsweb.access.logging.api;
|
||||
requires spring.beans;
|
||||
requires spring.boot.autoconfigure;
|
||||
requires spring.context;
|
||||
requires spring.boot;
|
||||
requires reactor.core;
|
||||
requires static lombok;
|
||||
requires fastjson;
|
||||
requires commons.collections;
|
||||
requires com.fasterxml.jackson.annotation;
|
||||
requires jakarta.annotation;
|
||||
requires org.slf4j;
|
||||
requires spring.aop;
|
||||
requires org.reactivestreams;
|
||||
requires spring.web;
|
||||
requires org.apache.commons.collections4;
|
||||
requires jakarta.validation;
|
||||
requires io.swagger.v3.oas.annotations;
|
||||
requires spring.webmvc;
|
||||
requires jakarta.servlet;
|
||||
requires org.apache.commons.codec;
|
||||
|
||||
exports org.hswebframework.web.authorization.basic.web;
|
||||
exports org.hswebframework.web.authorization.basic.aop;
|
||||
exports org.hswebframework.web.authorization.basic.configuration;
|
||||
exports org.hswebframework.web.authorization.basic.define;
|
||||
|
||||
opens org.hswebframework.web.authorization.basic.aop;
|
||||
opens org.hswebframework.web.authorization.basic.configuration;
|
||||
}
|
||||
@@ -61,113 +61,4 @@ public class AopAuthorizingControllerTest {
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFiledDeny() {
|
||||
SimpleAuthentication authentication = new SimpleAuthentication();
|
||||
|
||||
SimpleFieldFilterDataAccessConfig config = new SimpleFieldFilterDataAccessConfig();
|
||||
config.setAction("query");
|
||||
config.setFields(new HashSet<>(Arrays.asList("name")));
|
||||
|
||||
authentication.setUser(SimpleUser.builder().id("test").username("test").build());
|
||||
authentication.setPermissions(Arrays.asList(SimplePermission.builder()
|
||||
.actions(Collections.singleton("query"))
|
||||
.dataAccesses(Collections.singleton(config))
|
||||
.id("test").build()));
|
||||
|
||||
ReactiveAuthenticationHolder.setSupplier(new ReactiveAuthenticationSupplier() {
|
||||
@Override
|
||||
public Mono<Authentication> get(String userId) {
|
||||
return Mono.empty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Authentication> get() {
|
||||
return Mono.just(authentication);
|
||||
}
|
||||
});
|
||||
|
||||
testController.queryUser(new QueryParam())
|
||||
.map(Param::getExcludes)
|
||||
.as(StepVerifier::create)
|
||||
.expectNextMatches(f -> f.contains("name"))
|
||||
.verifyComplete();
|
||||
|
||||
testController.queryUser(Mono.just(new QueryParam()))
|
||||
.map(Param::getExcludes)
|
||||
.as(StepVerifier::create)
|
||||
.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.setSupplier(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)
|
||||
.map(CastUtil::<Collection<Object>>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();
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user