mirror of
https://github.com/hs-web/hsweb-framework.git
synced 2026-06-09 01:14:16 +08:00
refactor: 弃用数据权限控制,AuthorizingHandleBeforeEvent增加异步支持. (#298)
This commit is contained in:
@@ -64,43 +64,41 @@ public class AopAuthorizingController extends StaticMethodMatcherPointcutAdvisor
|
||||
MethodInterceptorHolder holder,
|
||||
AuthorizingContext context,
|
||||
Supplier<? extends Publisher<?>> invoker) {
|
||||
MethodInterceptorContext interceptorContext = holder.createParamContext(invoker.get());
|
||||
context.setParamContext(interceptorContext);
|
||||
return this
|
||||
.invokeReactive(
|
||||
Authentication
|
||||
.currentReactive()
|
||||
.switchIfEmpty(Mono.error(UnAuthorizedException.NoStackTrace::new))
|
||||
.flatMap(auth -> {
|
||||
context.setAuthentication(auth);
|
||||
//响应式不再支持数据权限控制
|
||||
return authorizingHandler.handRBACAsync(context);
|
||||
}),
|
||||
(Publisher<?>) interceptorContext.getInvokeResult());
|
||||
}
|
||||
|
||||
return Authentication
|
||||
.currentReactive()
|
||||
.switchIfEmpty(Mono.error(UnAuthorizedException::new))
|
||||
.flatMapMany(auth -> {
|
||||
context.setAuthentication(auth);
|
||||
Function<Runnable, Publisher> afterRuner = runnable -> {
|
||||
MethodInterceptorContext interceptorContext = holder.createParamContext(invoker.get());
|
||||
context.setParamContext(interceptorContext);
|
||||
runnable.run();
|
||||
return (Publisher<?>) interceptorContext.getInvokeResult();
|
||||
};
|
||||
if (context.getDefinition().getPhased() != Phased.after) {
|
||||
authorizingHandler.handRBAC(context);
|
||||
if (context.getDefinition().getResources().getPhased() != Phased.after) {
|
||||
authorizingHandler.handleDataAccess(context);
|
||||
return invoker.get();
|
||||
} else {
|
||||
return afterRuner.apply(() -> authorizingHandler.handleDataAccess(context));
|
||||
}
|
||||
private Publisher<?> invokeReactive(Mono<?> before, Publisher<?> source) {
|
||||
if (source instanceof Mono) {
|
||||
return before.then((Mono<Object>) source);
|
||||
}
|
||||
return before.thenMany(source);
|
||||
}
|
||||
|
||||
} else {
|
||||
if (context.getDefinition().getResources().getPhased() != Phased.after) {
|
||||
authorizingHandler.handleDataAccess(context);
|
||||
return invoker.get();
|
||||
} else {
|
||||
return afterRuner.apply(() -> {
|
||||
authorizingHandler.handRBAC(context);
|
||||
authorizingHandler.handleDataAccess(context);
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
private <T> T invokeReactive(MethodInvocation invocation) {
|
||||
if (Mono.class.isAssignableFrom(invocation.getMethod().getReturnType())) {
|
||||
return (T) Mono.defer(() -> doProceed(invocation));
|
||||
}
|
||||
if (Flux.class.isAssignableFrom(invocation.getMethod().getReturnType())) {
|
||||
return (T) Flux.defer(() -> doProceed(invocation));
|
||||
}
|
||||
return doProceed(invocation);
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
private <T> T doProceed(MethodInvocation invocation) {
|
||||
|
||||
return (T) invocation.proceed();
|
||||
}
|
||||
|
||||
@@ -110,9 +108,12 @@ public class AopAuthorizingController extends StaticMethodMatcherPointcutAdvisor
|
||||
|
||||
MethodInterceptorContext paramContext = holder.createParamContext();
|
||||
|
||||
AuthorizeDefinition definition = aopMethodAuthorizeDefinitionParser.parse(methodInvocation
|
||||
.getThis()
|
||||
.getClass(), methodInvocation.getMethod(), paramContext);
|
||||
AuthorizeDefinition definition = aopMethodAuthorizeDefinitionParser
|
||||
.parse(methodInvocation
|
||||
.getThis()
|
||||
.getClass(),
|
||||
methodInvocation.getMethod(),
|
||||
paramContext);
|
||||
Object result = null;
|
||||
boolean isControl = false;
|
||||
if (null != definition && !definition.isEmpty()) {
|
||||
@@ -123,16 +124,12 @@ public class AopAuthorizingController extends StaticMethodMatcherPointcutAdvisor
|
||||
Class<?> returnType = methodInvocation.getMethod().getReturnType();
|
||||
//handle reactive method
|
||||
if (Publisher.class.isAssignableFrom(returnType)) {
|
||||
Publisher publisher = handleReactive0(definition, holder, context, () -> doProceed(methodInvocation));
|
||||
if (Mono.class.isAssignableFrom(returnType)) {
|
||||
return Mono.from(publisher);
|
||||
} else if (Flux.class.isAssignableFrom(returnType)) {
|
||||
return Flux.from(publisher);
|
||||
}
|
||||
throw new UnsupportedOperationException("unsupported reactive type:" + returnType);
|
||||
return handleReactive0(definition, holder, context, () -> invokeReactive(methodInvocation));
|
||||
}
|
||||
|
||||
Authentication authentication = Authentication.current().orElseThrow(UnAuthorizedException::new);
|
||||
Authentication authentication = Authentication
|
||||
.current()
|
||||
.orElseThrow(UnAuthorizedException.NoStackTrace::new);
|
||||
|
||||
context.setAuthentication(authentication);
|
||||
isControl = true;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package org.hswebframework.web.authorization.basic.handler;
|
||||
|
||||
import org.hswebframework.web.authorization.define.AuthorizingContext;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
/**
|
||||
* aop方式权限控制处理器
|
||||
@@ -8,10 +9,17 @@ import org.hswebframework.web.authorization.define.AuthorizingContext;
|
||||
* @author zhouhao
|
||||
*/
|
||||
public interface AuthorizingHandler {
|
||||
|
||||
void handRBAC(AuthorizingContext context);
|
||||
|
||||
default Mono<Void> handRBACAsync(AuthorizingContext context) {
|
||||
return Mono.fromRunnable(() -> handRBAC(context));
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
void handleDataAccess(AuthorizingContext context);
|
||||
|
||||
@Deprecated
|
||||
default void handle(AuthorizingContext context) {
|
||||
handRBAC(context);
|
||||
handleDataAccess(context);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package org.hswebframework.web.authorization.basic.handler;
|
||||
|
||||
import lombok.SneakyThrows;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.hswebframework.web.authorization.Authentication;
|
||||
import org.hswebframework.web.authorization.Permission;
|
||||
@@ -14,6 +15,9 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* @author zhouhao
|
||||
@@ -51,15 +55,54 @@ public class DefaultAuthorizingHandler implements AuthorizingHandler {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Void> handRBACAsync(AuthorizingContext context) {
|
||||
return this
|
||||
.handleEventAsync(context, HandleType.RBAC)
|
||||
.doOnNext(handled -> {
|
||||
//没有自定义事件处理
|
||||
if (!handled) {
|
||||
handleRBAC(context.getAuthentication(), context.getDefinition());
|
||||
}
|
||||
})
|
||||
.then();
|
||||
}
|
||||
|
||||
private Mono<Boolean> handleEventAsync(AuthorizingContext context, HandleType type) {
|
||||
if (null != eventPublisher) {
|
||||
AuthorizingHandleBeforeEvent event = new AuthorizingHandleBeforeEvent(context, type);
|
||||
return event
|
||||
.publish(eventPublisher)
|
||||
.then(Mono.fromCallable(() -> {
|
||||
if (!event.isExecute()) {
|
||||
if (event.isAllow()) {
|
||||
return true;
|
||||
} else {
|
||||
throw new AccessDenyException.NoStackTrace(event.getMessage());
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}));
|
||||
}
|
||||
return Mono.just(false);
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
private boolean handleEvent(AuthorizingContext context, HandleType type) {
|
||||
if (null != eventPublisher) {
|
||||
AuthorizingHandleBeforeEvent event = new AuthorizingHandleBeforeEvent(context, type);
|
||||
eventPublisher.publishEvent(event);
|
||||
if (event.hasListener()) {
|
||||
event
|
||||
.getAsync()
|
||||
.toFuture()
|
||||
.get(10, TimeUnit.SECONDS);
|
||||
}
|
||||
if (!event.isExecute()) {
|
||||
if (event.isAllow()) {
|
||||
return true;
|
||||
} else {
|
||||
throw new AccessDenyException(event.getMessage());
|
||||
throw new AccessDenyException.NoStackTrace(event.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -82,21 +125,26 @@ public class DefaultAuthorizingHandler implements AuthorizingHandler {
|
||||
DataAccessController finalAccessController = dataAccessController;
|
||||
Authentication autz = context.getAuthentication();
|
||||
|
||||
boolean isAccess = context.getDefinition()
|
||||
.getResources()
|
||||
.getDataAccessResources()
|
||||
.stream()
|
||||
.allMatch(resource -> {
|
||||
Permission permission = autz.getPermission(resource.getId()).orElseThrow(AccessDenyException::new);
|
||||
return resource.getDataAccessAction()
|
||||
.stream()
|
||||
.allMatch(act -> permission.getDataAccesses(act.getId())
|
||||
.stream()
|
||||
.allMatch(dataAccessConfig -> finalAccessController.doAccess(dataAccessConfig, context)));
|
||||
boolean isAccess = context
|
||||
.getDefinition()
|
||||
.getResources()
|
||||
.getDataAccessResources()
|
||||
.stream()
|
||||
.allMatch(resource -> {
|
||||
Permission permission = autz
|
||||
.getPermission(resource.getId())
|
||||
.orElseThrow(AccessDenyException.NoStackTrace::new);
|
||||
return resource
|
||||
.getDataAccessAction()
|
||||
.stream()
|
||||
.allMatch(act -> permission
|
||||
.getDataAccesses(act.getId())
|
||||
.stream()
|
||||
.allMatch(dataAccessConfig -> finalAccessController.doAccess(dataAccessConfig, context)));
|
||||
|
||||
});
|
||||
});
|
||||
if (!isAccess) {
|
||||
throw new AccessDenyException(context.getDefinition().getMessage());
|
||||
throw new AccessDenyException.NoStackTrace(context.getDefinition().getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -106,7 +154,7 @@ public class DefaultAuthorizingHandler implements AuthorizingHandler {
|
||||
ResourcesDefinition resources = definition.getResources();
|
||||
|
||||
if (!resources.hasPermission(authentication)) {
|
||||
throw new AccessDenyException(definition.getMessage(),definition.getDescription());
|
||||
throw new AccessDenyException.NoStackTrace(definition.getMessage(), definition.getDescription());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,114 +60,114 @@ public class AopAuthorizingControllerTest {
|
||||
.expectNext("403")
|
||||
.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();
|
||||
|
||||
|
||||
}
|
||||
//
|
||||
// @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