mirror of
https://github.com/hs-web/hsweb-framework.git
synced 2026-05-19 06:18:15 +08:00
refactor: 优化权限控制
This commit is contained in:
@@ -111,9 +111,7 @@ public class AopAuthorizingController extends StaticMethodMatcherPointcutAdvisor
|
||||
MethodInterceptorContext paramContext = holder.createParamContext();
|
||||
|
||||
AuthorizeDefinition definition = aopMethodAuthorizeDefinitionParser
|
||||
.parse(methodInvocation
|
||||
.getThis()
|
||||
.getClass(),
|
||||
.parse(methodInvocation.getThis().getClass(),
|
||||
methodInvocation.getMethod(),
|
||||
paramContext);
|
||||
Object result = null;
|
||||
@@ -131,43 +129,26 @@ public class AopAuthorizingController extends StaticMethodMatcherPointcutAdvisor
|
||||
|
||||
Authentication authentication = Authentication
|
||||
.current()
|
||||
.orElseThrow(UnAuthorizedException.NoStackTrace::new);
|
||||
.orElse(null);
|
||||
|
||||
if (authentication == null) {
|
||||
// 允许匿名访问
|
||||
if (definition.allowAnonymous()) {
|
||||
return methodInvocation.proceed();
|
||||
}
|
||||
return new UnAuthorizedException.NoStackTrace();
|
||||
}
|
||||
|
||||
context.setAuthentication(authentication);
|
||||
isControl = true;
|
||||
|
||||
Phased dataAccessPhased = definition.getResources().getPhased();
|
||||
if (definition.getPhased() == Phased.before) {
|
||||
//RDAC before
|
||||
authorizingHandler.handRBAC(context);
|
||||
|
||||
//方法调用前验证数据权限
|
||||
if (dataAccessPhased == Phased.before) {
|
||||
authorizingHandler.handleDataAccess(context);
|
||||
}
|
||||
|
||||
result = methodInvocation.proceed();
|
||||
|
||||
//方法调用后验证数据权限
|
||||
if (dataAccessPhased == Phased.after) {
|
||||
context.setParamContext(holder.createParamContext(result));
|
||||
authorizingHandler.handleDataAccess(context);
|
||||
}
|
||||
} else {
|
||||
//方法调用前验证数据权限
|
||||
if (dataAccessPhased == Phased.before) {
|
||||
authorizingHandler.handleDataAccess(context);
|
||||
}
|
||||
|
||||
result = methodInvocation.proceed();
|
||||
context.setParamContext(holder.createParamContext(result));
|
||||
|
||||
authorizingHandler.handRBAC(context);
|
||||
|
||||
//方法调用后验证数据权限
|
||||
if (dataAccessPhased == Phased.after) {
|
||||
authorizingHandler.handleDataAccess(context);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!isControl) {
|
||||
|
||||
@@ -92,7 +92,7 @@ public class DefaultAopMethodAuthorizeDefinitionParser implements AopMethodAutho
|
||||
}
|
||||
}
|
||||
|
||||
public CacheKey buildCacheKey(Class<?> target, Method method) {
|
||||
CacheKey buildCacheKey(Class<?> target, Method method) {
|
||||
return new CacheKey(ClassUtils.getUserClass(target), method);
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,8 @@ import org.springframework.core.annotation.AnnotatedElementUtils;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.annotation.Repeatable;
|
||||
import java.lang.reflect.AnnotatedElement;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.*;
|
||||
import java.util.function.Function;
|
||||
@@ -20,7 +22,8 @@ public class AopAuthorizeDefinitionParser {
|
||||
Authorize.class,
|
||||
Dimension.class,
|
||||
Resource.class,
|
||||
ResourceAction.class
|
||||
ResourceAction.class,
|
||||
Dimensions.class
|
||||
));
|
||||
|
||||
private final Set<Annotation> methodAnnotation;
|
||||
@@ -37,10 +40,8 @@ public class AopAuthorizeDefinitionParser {
|
||||
definition = new DefaultBasicAuthorizeDefinition();
|
||||
definition.setTargetClass(targetClass);
|
||||
definition.setTargetMethod(method);
|
||||
|
||||
methodAnnotation = AnnotatedElementUtils.findAllMergedAnnotations(method, types);
|
||||
|
||||
classAnnotation = AnnotatedElementUtils.findAllMergedAnnotations(targetClass, types);
|
||||
methodAnnotation = loadAnnotations(method);
|
||||
classAnnotation = loadAnnotations(targetClass);
|
||||
|
||||
classAnnotationGroup = classAnnotation
|
||||
.stream()
|
||||
@@ -51,6 +52,23 @@ public class AopAuthorizeDefinitionParser {
|
||||
.collect(Collectors.groupingBy(Annotation::annotationType));
|
||||
}
|
||||
|
||||
private Set<Annotation> loadAnnotations(AnnotatedElement element) {
|
||||
return types
|
||||
.stream()
|
||||
.flatMap(s -> {
|
||||
if (s.isAnnotationPresent(Repeatable.class)) {
|
||||
return AnnotatedElementUtils
|
||||
.findMergedRepeatableAnnotations(element, s)
|
||||
.stream();
|
||||
}
|
||||
return AnnotatedElementUtils
|
||||
.findAllMergedAnnotations(element, s)
|
||||
.stream();
|
||||
})
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
private void initClassAnnotation() {
|
||||
for (Annotation annotation : classAnnotation) {
|
||||
if (annotation instanceof Authorize) {
|
||||
@@ -73,12 +91,16 @@ public class AopAuthorizeDefinitionParser {
|
||||
if (annotation instanceof Dimension) {
|
||||
definition.putAnnotation(((Dimension) annotation));
|
||||
}
|
||||
if (annotation instanceof Dimensions) {
|
||||
definition.putAnnotation(((Dimensions) annotation));
|
||||
}
|
||||
|
||||
if (annotation instanceof ResourceAction) {
|
||||
getAnnotationByType(Resource.class)
|
||||
.map(res -> definition.getResources().getResource(res.id()).orElse(null))
|
||||
.filter(Objects::nonNull)
|
||||
.forEach(res -> {
|
||||
definition.putAnnotation(res, (ResourceAction) annotation);
|
||||
definition.putAnnotation(res, (ResourceAction) annotation);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,10 +61,17 @@ public class DefaultBasicAuthorizeDefinition implements AopAuthorizeDefinition {
|
||||
return parser.parse();
|
||||
}
|
||||
|
||||
public void putAnnotation(Dimensions ann) {
|
||||
dimensions.setLogical(ann.logical());
|
||||
if (ann.description().length > 0) {
|
||||
dimensions.setDescription(String.join("", ann.description()));
|
||||
}
|
||||
}
|
||||
|
||||
public void putAnnotation(Authorize ann) {
|
||||
if (!ann.merge()) {
|
||||
getResources().getResources().clear();
|
||||
getDimensions().getDimensions().clear();
|
||||
getResources().clear();
|
||||
getDimensions().clear();
|
||||
}
|
||||
setPhased(ann.phased());
|
||||
getResources().setPhased(ann.phased());
|
||||
@@ -81,7 +88,7 @@ public class DefaultBasicAuthorizeDefinition implements AopAuthorizeDefinition {
|
||||
|
||||
public void putAnnotation(Dimension ann) {
|
||||
if (ann.ignore()) {
|
||||
getDimensions().getDimensions().clear();
|
||||
getDimensions().clear();
|
||||
return;
|
||||
}
|
||||
DimensionDefinition definition = new DimensionDefinition();
|
||||
|
||||
@@ -5,10 +5,8 @@ import lombok.extern.slf4j.Slf4j;
|
||||
import org.hswebframework.web.authorization.Authentication;
|
||||
import org.hswebframework.web.authorization.Permission;
|
||||
import org.hswebframework.web.authorization.access.DataAccessController;
|
||||
import org.hswebframework.web.authorization.define.AuthorizeDefinition;
|
||||
import org.hswebframework.web.authorization.define.AuthorizingContext;
|
||||
import org.hswebframework.web.authorization.define.HandleType;
|
||||
import org.hswebframework.web.authorization.define.ResourcesDefinition;
|
||||
import org.hswebframework.web.authorization.annotation.Logical;
|
||||
import org.hswebframework.web.authorization.define.*;
|
||||
import org.hswebframework.web.authorization.events.AuthorizingHandleBeforeEvent;
|
||||
import org.hswebframework.web.authorization.exception.AccessDenyException;
|
||||
import org.slf4j.Logger;
|
||||
@@ -17,6 +15,7 @@ import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
@@ -93,10 +92,7 @@ public class DefaultAuthorizingHandler implements AuthorizingHandler {
|
||||
AuthorizingHandleBeforeEvent event = new AuthorizingHandleBeforeEvent(context, type);
|
||||
eventPublisher.publishEvent(event);
|
||||
if (event.hasListener()) {
|
||||
event
|
||||
.getAsync()
|
||||
.toFuture()
|
||||
.get(10, TimeUnit.SECONDS);
|
||||
event.getAsync().block();
|
||||
}
|
||||
if (!event.isExecute()) {
|
||||
if (event.isAllow()) {
|
||||
@@ -109,51 +105,39 @@ public class DefaultAuthorizingHandler implements AuthorizingHandler {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public void handleDataAccess(AuthorizingContext context) {
|
||||
|
||||
if (dataAccessController == null) {
|
||||
return;
|
||||
}
|
||||
if (context.getDefinition().getResources() == null) {
|
||||
return;
|
||||
}
|
||||
if (handleEvent(context, HandleType.DATA)) {
|
||||
return;
|
||||
}
|
||||
|
||||
DataAccessController finalAccessController = dataAccessController;
|
||||
Authentication autz = context.getAuthentication();
|
||||
|
||||
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.NoStackTrace(context.getDefinition().getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected void handleRBAC(Authentication authentication, AuthorizeDefinition definition) {
|
||||
|
||||
ResourcesDefinition resources = definition.getResources();
|
||||
|
||||
// 判断权限
|
||||
if (!resources.hasPermission(authentication)) {
|
||||
throw new AccessDenyException.NoStackTrace(definition.getMessage(), definition.getDescription());
|
||||
}
|
||||
|
||||
DimensionsDefinition dd = definition.getDimensions();
|
||||
// 判断维度
|
||||
if (dd != null && !dd.isEmpty()) {
|
||||
if (!dd.hasDimension(
|
||||
(type, logical, dimensionIds) ->
|
||||
hasDimensions(authentication, type, logical, dimensionIds))) {
|
||||
|
||||
throw new AccessDenyException
|
||||
.NoStackTrace(definition.getMessage(), definition.getDimensions().toString());
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean hasDimensions(Authentication auth, String type, Logical logical, Set<String> dimensionIds) {
|
||||
if (logical == Logical.AND) {
|
||||
return auth.hasAllDimension(type, dimensionIds);
|
||||
}
|
||||
return auth.hasAnyDimension(type, dimensionIds);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,25 +16,27 @@ public class DefaultBasicAuthorizeDefinitionTest {
|
||||
@SneakyThrows
|
||||
public void testCustomAnn() {
|
||||
AopAuthorizeDefinition definition =
|
||||
DefaultBasicAuthorizeDefinition.from(TestController.class, TestController.class.getMethod("test"));
|
||||
DefaultBasicAuthorizeDefinition.from(TestController.class, TestController.class.getMethod("test"));
|
||||
|
||||
ResourceDefinition resource = definition.getResources()
|
||||
.getResource("test").orElseThrow(NullPointerException::new);
|
||||
ResourceDefinition resource = definition
|
||||
.getResources()
|
||||
.getResource("test").orElseThrow(NullPointerException::new);
|
||||
|
||||
Assert.assertNotNull(resource);
|
||||
|
||||
Assert.assertTrue(resource.hasAction(Arrays.asList("add")));
|
||||
System.out.println(definition.getDimensions());
|
||||
Assert.assertFalse(definition.getDimensions().isEmpty());
|
||||
Assert.assertEquals(1, definition.getDimensions().getDimensions().size());
|
||||
|
||||
|
||||
Assert.assertTrue(resource.getAction("add")
|
||||
.map(act->act.getDataAccess().getType("user_own_data"))
|
||||
.isPresent());
|
||||
}
|
||||
|
||||
@Test
|
||||
@SneakyThrows
|
||||
public void testNoMerge() {
|
||||
AopAuthorizeDefinition definition =
|
||||
DefaultBasicAuthorizeDefinition.from(TestController.class, TestController.class.getMethod("noMerge"));
|
||||
DefaultBasicAuthorizeDefinition.from(TestController.class, TestController.class.getMethod("noMerge"));
|
||||
Assert.assertTrue(definition.getResources().isEmpty());
|
||||
}
|
||||
|
||||
@@ -43,17 +45,19 @@ public class DefaultBasicAuthorizeDefinitionTest {
|
||||
public class TestController implements GenericController {
|
||||
|
||||
@Authorize(merge = false)
|
||||
public void noMerge(){
|
||||
public void noMerge() {
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
public interface GenericController {
|
||||
|
||||
@CreateAction
|
||||
@UserOwnData
|
||||
default void test(){
|
||||
@RequiresRoles("test")
|
||||
@RequiresRoles("test2")
|
||||
default void test() {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user