mirror of
https://github.com/hs-web/hsweb-framework.git
synced 2026-06-02 10:57:49 +08:00
优化权限解析
This commit is contained in:
@@ -0,0 +1,155 @@
|
||||
package org.hswebframework.web.authorization.basic.define;
|
||||
|
||||
import org.hswebframework.web.authorization.annotation.*;
|
||||
import org.hswebframework.web.authorization.define.AopAuthorizeDefinition;
|
||||
import org.hswebframework.web.authorization.define.ResourceActionDefinition;
|
||||
import org.hswebframework.web.authorization.define.ResourceDefinition;
|
||||
import org.springframework.core.annotation.AnnotatedElementUtils;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.*;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class AopAuthorizeDefinitionParser {
|
||||
|
||||
private static final Set<Class<? extends Annotation>> types = new HashSet<>(Arrays.asList(
|
||||
Authorize.class,
|
||||
DataAccess.class,
|
||||
Dimension.class,
|
||||
Resource.class,
|
||||
ResourceAction.class,
|
||||
DataAccessType.class
|
||||
));
|
||||
|
||||
private Set<Annotation> methodAnnotation;
|
||||
|
||||
private Set<Annotation> classAnnotation;
|
||||
|
||||
private Map<Class<? extends Annotation>, List<Annotation>> classAnnotationGroup;
|
||||
|
||||
private Map<Class<? extends Annotation>, List<Annotation>> methodAnnotationGroup;
|
||||
|
||||
private DefaultBasicAuthorizeDefinition definition;
|
||||
|
||||
AopAuthorizeDefinitionParser(Class<?> targetClass, Method method) {
|
||||
definition = new DefaultBasicAuthorizeDefinition();
|
||||
definition.setTargetClass(targetClass);
|
||||
definition.setTargetMethod(method);
|
||||
|
||||
methodAnnotation = AnnotatedElementUtils.findAllMergedAnnotations(method, types);
|
||||
|
||||
classAnnotation = AnnotatedElementUtils.findAllMergedAnnotations(targetClass, types);
|
||||
|
||||
classAnnotationGroup = classAnnotation
|
||||
.stream()
|
||||
.collect(Collectors.groupingBy(Annotation::annotationType));
|
||||
|
||||
methodAnnotationGroup = methodAnnotation
|
||||
.stream()
|
||||
.collect(Collectors.groupingBy(Annotation::annotationType));
|
||||
}
|
||||
|
||||
private void initClassAnnotation() {
|
||||
for (Annotation annotation : classAnnotation) {
|
||||
if (annotation instanceof Authorize) {
|
||||
definition.putAnnotation(((Authorize) annotation));
|
||||
}
|
||||
if (annotation instanceof Resource) {
|
||||
definition.putAnnotation(((Resource) annotation));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void initMethodAnnotation() {
|
||||
for (Annotation annotation : methodAnnotation) {
|
||||
if (annotation instanceof Authorize) {
|
||||
definition.putAnnotation(((Authorize) annotation));
|
||||
}
|
||||
if (annotation instanceof Resource) {
|
||||
definition.putAnnotation(((Resource) annotation));
|
||||
}
|
||||
if (annotation instanceof Dimension) {
|
||||
definition.putAnnotation(((Dimension) annotation));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void initClassDataAccessAnnotation(){
|
||||
for (Annotation annotation : classAnnotation) {
|
||||
if (annotation instanceof DataAccessType ||
|
||||
annotation instanceof DataAccess) {
|
||||
for (ResourceDefinition resource : definition.getResources().getResources()) {
|
||||
for (ResourceActionDefinition action : resource.getActions()) {
|
||||
if (annotation instanceof DataAccessType) {
|
||||
definition.putAnnotation(action, (DataAccessType) annotation);
|
||||
} else {
|
||||
definition.putAnnotation(action, (DataAccess) annotation);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void initMethodDataAccessAnnotation() {
|
||||
for (Annotation annotation : methodAnnotation) {
|
||||
|
||||
if (annotation instanceof ResourceAction) {
|
||||
getAnnotationByType(Resource.class)
|
||||
.map(res -> definition.getResources().getResource(res.id()).orElse(null))
|
||||
.filter(Objects::nonNull)
|
||||
.forEach(res -> {
|
||||
ResourceAction ra = (ResourceAction) annotation;
|
||||
ResourceActionDefinition action = definition.putAnnotation(res, ra);
|
||||
getAnnotationByType(DataAccessType.class)
|
||||
.findFirst()
|
||||
.ifPresent(dat -> definition.putAnnotation(action, dat));
|
||||
});
|
||||
}
|
||||
Optional<ResourceActionDefinition> actionDefinition = getAnnotationByType(Resource.class)
|
||||
.map(res -> definition.getResources().getResource(res.id()).orElse(null))
|
||||
.filter(Objects::nonNull)
|
||||
.flatMap(res -> getAnnotationByType(ResourceAction.class)
|
||||
.map(ra -> res.getAction(ra.id())
|
||||
.orElse(null))
|
||||
)
|
||||
.filter(Objects::nonNull)
|
||||
.findFirst();
|
||||
|
||||
if (annotation instanceof DataAccessType) {
|
||||
actionDefinition.ifPresent(ra -> definition.putAnnotation(ra, (DataAccessType) annotation));
|
||||
}
|
||||
|
||||
if (annotation instanceof DataAccess) {
|
||||
actionDefinition.ifPresent(ra -> {
|
||||
definition.putAnnotation(ra, (DataAccess) annotation);
|
||||
getAnnotationByType(DataAccessType.class)
|
||||
.findFirst()
|
||||
.ifPresent(dat -> definition.putAnnotation(ra, dat));
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
AopAuthorizeDefinition parse() {
|
||||
initClassAnnotation();
|
||||
initClassDataAccessAnnotation();
|
||||
initMethodAnnotation();
|
||||
initMethodDataAccessAnnotation();
|
||||
|
||||
return definition;
|
||||
}
|
||||
|
||||
|
||||
private <T extends Annotation> Stream<T> getAnnotationByType(Class<T> type) {
|
||||
return Optional.ofNullable(methodAnnotationGroup.getOrDefault(type, classAnnotationGroup.get(type)))
|
||||
.map(Collection::stream)
|
||||
.orElseGet(Stream::empty)
|
||||
.map(type::cast);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -5,12 +5,14 @@ import lombok.*;
|
||||
import org.hswebframework.web.authorization.annotation.*;
|
||||
import org.hswebframework.web.authorization.define.*;
|
||||
import org.springframework.core.annotation.AnnotatedElementUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.*;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* 默认权限权限定义
|
||||
@@ -54,99 +56,7 @@ public class DefaultBasicAuthorizeDefinition implements AopAuthorizeDefinition {
|
||||
));
|
||||
|
||||
public static AopAuthorizeDefinition from(Class targetClass, Method method) {
|
||||
DefaultBasicAuthorizeDefinition definition = new DefaultBasicAuthorizeDefinition();
|
||||
definition.setTargetClass(targetClass);
|
||||
definition.setTargetMethod(method);
|
||||
|
||||
Set<Annotation> methodAnnotation = AnnotatedElementUtils.findAllMergedAnnotations(method, types);
|
||||
|
||||
Set<Annotation> classAnnotation = AnnotatedElementUtils.findAllMergedAnnotations(targetClass, types);
|
||||
|
||||
Map<Class, Annotation> classAnnotationMap = classAnnotation
|
||||
.stream()
|
||||
.collect(Collectors.toMap(Annotation::annotationType, Function.identity()));
|
||||
|
||||
Map<Class, Annotation> mapping = methodAnnotation
|
||||
.stream()
|
||||
.collect(Collectors.toMap(Annotation::annotationType, Function.identity()));
|
||||
|
||||
for (Annotation annotation : classAnnotation) {
|
||||
if (annotation instanceof Authorize) {
|
||||
definition.putAnnotation(((Authorize) annotation));
|
||||
}
|
||||
if (annotation instanceof Resource) {
|
||||
definition.putAnnotation(((Resource) annotation));
|
||||
}
|
||||
}
|
||||
|
||||
for (Annotation annotation : methodAnnotation) {
|
||||
if (annotation instanceof Authorize) {
|
||||
definition.putAnnotation(((Authorize) annotation));
|
||||
}
|
||||
if (annotation instanceof Resource) {
|
||||
definition.putAnnotation(((Resource) annotation));
|
||||
}
|
||||
if (annotation instanceof Dimension) {
|
||||
definition.putAnnotation(((Dimension) annotation));
|
||||
}
|
||||
}
|
||||
|
||||
for (Annotation annotation : methodAnnotation) {
|
||||
|
||||
if (annotation instanceof ResourceAction) {
|
||||
Optional.ofNullable(mapping.getOrDefault(Resource.class, classAnnotationMap.get(Resource.class)))
|
||||
.map(Resource.class::cast)
|
||||
.flatMap(res -> definition.getResources().getResource(res.id()))
|
||||
.ifPresent(res -> {
|
||||
|
||||
ResourceAction ra = (ResourceAction) annotation;
|
||||
ResourceActionDefinition action = definition.putAnnotation(res, ra);
|
||||
|
||||
Optional.ofNullable(mapping.get(DataAccessType.class))
|
||||
.map(DataAccessType.class::cast)
|
||||
.ifPresent(dat -> definition.putAnnotation(action, dat));
|
||||
});
|
||||
}
|
||||
Optional<ResourceActionDefinition> actionDefinition = Optional.ofNullable(mapping.getOrDefault(Resource.class, classAnnotationMap.get(Resource.class)))
|
||||
.map(Resource.class::cast)
|
||||
.flatMap(res -> definition.getResources().getResource(res.id()))
|
||||
.flatMap(res -> Optional.ofNullable(mapping.get(ResourceAction.class))
|
||||
.map(ResourceAction.class::cast)
|
||||
.flatMap(ra -> res.getAction(ra.id())));
|
||||
|
||||
if (annotation instanceof DataAccessType) {
|
||||
actionDefinition.ifPresent(ra -> definition.putAnnotation(ra, (DataAccessType) annotation));
|
||||
}
|
||||
|
||||
if (annotation instanceof DataAccess) {
|
||||
actionDefinition.ifPresent(ra -> {
|
||||
definition.putAnnotation(ra, (DataAccess) annotation);
|
||||
Optional.ofNullable(mapping.get(DataAccessType.class))
|
||||
.map(DataAccessType.class::cast)
|
||||
.ifPresent(dat -> definition.putAnnotation(ra, dat));
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
for (Annotation annotation : classAnnotation) {
|
||||
if (annotation instanceof DataAccessType||
|
||||
annotation instanceof DataAccess) {
|
||||
for (ResourceDefinition resource : definition.getResources().getResources()) {
|
||||
for (ResourceActionDefinition action : resource.getActions()) {
|
||||
if(annotation instanceof DataAccessType) {
|
||||
definition.putAnnotation(action, (DataAccessType) annotation);
|
||||
}else{
|
||||
definition.putAnnotation(action, (DataAccess) annotation);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return definition;
|
||||
return new AopAuthorizeDefinitionParser(targetClass,method).parse();
|
||||
}
|
||||
|
||||
public void putAnnotation(Authorize ann) {
|
||||
@@ -208,18 +118,27 @@ public class DefaultBasicAuthorizeDefinition implements AopAuthorizeDefinition {
|
||||
}
|
||||
DataAccessTypeDefinition typeDefinition = new DataAccessTypeDefinition();
|
||||
for (DataAccessType dataAccessType : ann.type()) {
|
||||
if(dataAccessType.ignore()){
|
||||
continue;
|
||||
}
|
||||
typeDefinition.setId(dataAccessType.id());
|
||||
typeDefinition.setName(dataAccessType.name());
|
||||
typeDefinition.setController(dataAccessType.controller());
|
||||
typeDefinition.setConfiguration(dataAccessType.configuration());
|
||||
typeDefinition.setDescription(String.join("\n", dataAccessType.description()));
|
||||
}
|
||||
if(StringUtils.isEmpty(typeDefinition.getId())){
|
||||
return;
|
||||
}
|
||||
definition.getDataAccess()
|
||||
.getDataAccessTypes()
|
||||
.add(typeDefinition);
|
||||
}
|
||||
|
||||
public void putAnnotation(ResourceActionDefinition definition, DataAccessType dataAccessType) {
|
||||
if(dataAccessType.ignore()){
|
||||
return;
|
||||
}
|
||||
DataAccessTypeDefinition typeDefinition = new DataAccessTypeDefinition();
|
||||
typeDefinition.setId(dataAccessType.id());
|
||||
typeDefinition.setName(dataAccessType.name());
|
||||
|
||||
@@ -10,6 +10,7 @@ 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.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.test.StepVerifier;
|
||||
@@ -32,7 +33,7 @@ public class AopAuthorizingControllerTest {
|
||||
authentication.setUser(SimpleUser.builder().id("test").username("test").build());
|
||||
// authentication.setPermissions(Arrays.asList(SimplePermission.builder().id("test").build()));
|
||||
authentication.setPermissions(Collections.emptyList());
|
||||
ReactiveAuthenticationHolder.addSupplier(new ReactiveAuthenticationSupplier() {
|
||||
ReactiveAuthenticationHolder.setSupplier(new ReactiveAuthenticationSupplier() {
|
||||
@Override
|
||||
public Mono<Authentication> get(String userId) {
|
||||
return Mono.empty();
|
||||
@@ -73,7 +74,7 @@ public class AopAuthorizingControllerTest {
|
||||
.dataAccesses(Collections.singleton(config))
|
||||
.id("test").build()));
|
||||
|
||||
ReactiveAuthenticationHolder.addSupplier(new ReactiveAuthenticationSupplier() {
|
||||
ReactiveAuthenticationHolder.setSupplier(new ReactiveAuthenticationSupplier() {
|
||||
@Override
|
||||
public Mono<Authentication> get(String userId) {
|
||||
return Mono.empty();
|
||||
@@ -109,7 +110,7 @@ public class AopAuthorizingControllerTest {
|
||||
DimensionDataAccessConfig config2 = new DimensionDataAccessConfig();
|
||||
config2.setAction("save");
|
||||
config2.setScopeType("role");
|
||||
ReactiveAuthenticationHolder.addSupplier(new ReactiveAuthenticationSupplier() {
|
||||
ReactiveAuthenticationHolder.setSupplier(new ReactiveAuthenticationSupplier() {
|
||||
@Override
|
||||
public Mono<Authentication> get(String userId) {
|
||||
return Mono.empty();
|
||||
|
||||
@@ -33,6 +33,7 @@ public class TestController implements ReactiveCrudController<TestEntity, String
|
||||
|
||||
@QueryAction
|
||||
@FieldDataAccess
|
||||
@DimensionDataAccess(ignore = true)
|
||||
public Mono<QueryParam> queryUser(QueryParam queryParam) {
|
||||
return Mono.just(queryParam);
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ public class DefaultBasicAuthorizeDefinitionTest {
|
||||
Assert.assertTrue(resource.hasAction(Arrays.asList("add")));
|
||||
|
||||
Assert.assertTrue(resource.getAction("add")
|
||||
.map(act->act.getDataAccess().getType("user_own"))
|
||||
.map(act->act.getDataAccess().getType("user_own_data"))
|
||||
.isPresent());
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user