mirror of
https://github.com/hs-web/hsweb-framework.git
synced 2026-06-03 19:33:16 +08:00
refactor: 优化权限控制
This commit is contained in:
@@ -17,6 +17,7 @@
|
||||
|
||||
package org.hswebframework.web.authorization;
|
||||
|
||||
import org.hswebframework.web.authorization.annotation.Logical;
|
||||
import org.springframework.util.StringUtils;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
@@ -93,18 +94,37 @@ public interface Authentication extends Serializable {
|
||||
* @return 用户持有的权限集合
|
||||
*/
|
||||
List<Permission> getPermissions();
|
||||
|
||||
|
||||
default boolean hasDimension(String type, String... id) {
|
||||
return hasDimension(type, Arrays.asList(id));
|
||||
return hasAnyDimension(type, Arrays.asList(id));
|
||||
}
|
||||
|
||||
default boolean hasAllDimension(String type, Collection<String> id) {
|
||||
if (id.isEmpty()) {
|
||||
return !getDimensions(type).isEmpty();
|
||||
}
|
||||
return getDimensions(type)
|
||||
.stream()
|
||||
.allMatch(p -> id.contains(p.getId()));
|
||||
}
|
||||
|
||||
default boolean hasAnyDimension(String type, Collection<String> id) {
|
||||
if (id.isEmpty()) {
|
||||
return !getDimensions(type).isEmpty();
|
||||
}
|
||||
return getDimensions(type)
|
||||
.stream()
|
||||
.anyMatch(p -> id.contains(p.getId()));
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
default boolean hasDimension(String type, Collection<String> id) {
|
||||
if (id.isEmpty()) {
|
||||
return !getDimensions(type).isEmpty();
|
||||
}
|
||||
return getDimensions(type)
|
||||
.stream()
|
||||
.anyMatch(p -> id.contains(p.getId()));
|
||||
.stream()
|
||||
.anyMatch(p -> id.contains(p.getId()));
|
||||
}
|
||||
|
||||
default boolean hasDimension(DimensionType type, String id) {
|
||||
@@ -116,9 +136,9 @@ public interface Authentication extends Serializable {
|
||||
return Optional.empty();
|
||||
}
|
||||
return getDimensions()
|
||||
.stream()
|
||||
.filter(dimension -> dimension.getId().equals(id) && type.equalsIgnoreCase(dimension.getType().getId()))
|
||||
.findFirst();
|
||||
.stream()
|
||||
.filter(dimension -> dimension.getId().equals(id) && type.equalsIgnoreCase(dimension.getType().getId()))
|
||||
.findFirst();
|
||||
}
|
||||
|
||||
default Optional<Dimension> getDimension(DimensionType type, String id) {
|
||||
@@ -126,9 +146,9 @@ public interface Authentication extends Serializable {
|
||||
return Optional.empty();
|
||||
}
|
||||
return getDimensions()
|
||||
.stream()
|
||||
.filter(dimension -> dimension.getId().equals(id) && type.isSameType(dimension.getType()))
|
||||
.findFirst();
|
||||
.stream()
|
||||
.filter(dimension -> dimension.getId().equals(id) && type.isSameType(dimension.getType()))
|
||||
.findFirst();
|
||||
}
|
||||
|
||||
|
||||
@@ -137,9 +157,9 @@ public interface Authentication extends Serializable {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return getDimensions()
|
||||
.stream()
|
||||
.filter(dimension -> dimension.getType().isSameType(type))
|
||||
.collect(Collectors.toList());
|
||||
.stream()
|
||||
.filter(dimension -> dimension.getType().isSameType(type))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
default List<Dimension> getDimensions(DimensionType type) {
|
||||
@@ -147,9 +167,9 @@ public interface Authentication extends Serializable {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return getDimensions()
|
||||
.stream()
|
||||
.filter(dimension -> dimension.getType().isSameType(type))
|
||||
.collect(Collectors.toList());
|
||||
.stream()
|
||||
.filter(dimension -> dimension.getType().isSameType(type))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
|
||||
@@ -164,9 +184,9 @@ public interface Authentication extends Serializable {
|
||||
return Optional.empty();
|
||||
}
|
||||
return getPermissions()
|
||||
.stream()
|
||||
.filter(permission -> permission.getId().equals(id))
|
||||
.findAny();
|
||||
.stream()
|
||||
.filter(permission -> permission.getId().equals(id))
|
||||
.findAny();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -179,8 +199,8 @@ public interface Authentication extends Serializable {
|
||||
default boolean hasPermission(String permissionId, String... actions) {
|
||||
return hasPermission(permissionId,
|
||||
actions.length == 0
|
||||
? Collections.emptyList()
|
||||
: Arrays.asList(actions));
|
||||
? Collections.emptyList()
|
||||
: Arrays.asList(actions));
|
||||
}
|
||||
|
||||
default boolean hasPermission(String permissionId, Collection<String> actions) {
|
||||
@@ -190,8 +210,8 @@ public interface Authentication extends Serializable {
|
||||
}
|
||||
if (Objects.equals(permissionId, permission.getId())) {
|
||||
return actions.isEmpty()
|
||||
|| permission.getActions().containsAll(actions)
|
||||
|| permission.getActions().contains("*");
|
||||
|| permission.getActions().containsAll(actions)
|
||||
|| permission.getActions().contains("*");
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
||||
@@ -18,7 +18,7 @@ public interface AuthenticationPredicate extends Predicate<Authentication> {
|
||||
}
|
||||
|
||||
static AuthenticationPredicate dimension(String dimension, String... id) {
|
||||
return autz -> autz.hasDimension(dimension, Arrays.asList(id));
|
||||
return autz -> autz.hasAnyDimension(dimension, Arrays.asList(id));
|
||||
}
|
||||
|
||||
static AuthenticationPredicate permission(String permissionId, String... actions) {
|
||||
|
||||
@@ -1,20 +1,67 @@
|
||||
package org.hswebframework.web.authorization.annotation;
|
||||
|
||||
import org.hswebframework.web.authorization.DimensionType;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
import static java.lang.annotation.ElementType.*;
|
||||
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||
|
||||
/**
|
||||
* 请使用注解继承方式使用此注解
|
||||
*
|
||||
* @author zhouhao
|
||||
* @see RequiresRoles
|
||||
* @since 4.0
|
||||
*/
|
||||
@Target({ElementType.ANNOTATION_TYPE})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Inherited
|
||||
@Documented
|
||||
@Repeatable(value = Dimension.List.class)
|
||||
public @interface Dimension {
|
||||
|
||||
/**
|
||||
* 维度类型标识,如: role,org
|
||||
*
|
||||
* @return 维度类型
|
||||
* @see org.hswebframework.web.authorization.Dimension#getType()
|
||||
* @see DimensionType#getId()
|
||||
* @see org.hswebframework.web.authorization.Authentication#hasDimension(String, String...)
|
||||
*/
|
||||
String type();
|
||||
|
||||
/**
|
||||
* 具体的维度ID,如: 角色ID,组织ID
|
||||
*
|
||||
* @return 维度ID
|
||||
* @see org.hswebframework.web.authorization.Dimension#getId()
|
||||
* @see org.hswebframework.web.authorization.Authentication#hasDimension(String, String...)
|
||||
*/
|
||||
String[] id() default {};
|
||||
|
||||
/**
|
||||
* 配置了多个ID时的判断逻辑,默认为任意满足则认为有权限.
|
||||
*
|
||||
* @return Logical
|
||||
*/
|
||||
Logical logical() default Logical.DEFAULT;
|
||||
|
||||
/**
|
||||
* @return 说明
|
||||
*/
|
||||
String[] description() default {};
|
||||
|
||||
/**
|
||||
* @return 是否忽略
|
||||
*/
|
||||
boolean ignore() default false;
|
||||
|
||||
@Target({ANNOTATION_TYPE})
|
||||
@Retention(RUNTIME)
|
||||
@Documented
|
||||
@Inherited
|
||||
@interface List {
|
||||
Dimension[] value() default {};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package org.hswebframework.web.authorization.annotation;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
import static java.lang.annotation.ElementType.*;
|
||||
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||
|
||||
/**
|
||||
* 标记多个维度的权限控制相关配置
|
||||
*
|
||||
* @author zhouhao
|
||||
* @since 5.0.1
|
||||
*/
|
||||
@Target({ElementType.METHOD, TYPE, ANNOTATION_TYPE, FIELD})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Inherited
|
||||
@Documented
|
||||
public @interface Dimensions {
|
||||
|
||||
/**
|
||||
* 存在多个维度时的判断逻辑,默认任意一个满足则认为有权限
|
||||
*
|
||||
* @return Logical
|
||||
*/
|
||||
Logical logical() default Logical.DEFAULT;
|
||||
|
||||
/**
|
||||
* @return 针对当前配置的说明信息
|
||||
*/
|
||||
String[] description() default {};
|
||||
|
||||
}
|
||||
@@ -5,17 +5,49 @@ import org.springframework.core.annotation.AliasFor;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
@Target({ElementType.METHOD})
|
||||
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||
|
||||
/**
|
||||
* 注解根据角色维度进行权限控制,具有权限的用户才可访问对应的方法.
|
||||
*
|
||||
* <pre>{@code
|
||||
* @RequiresRoles("admin")
|
||||
* public Mono<Void> handleRequest(){
|
||||
*
|
||||
* }
|
||||
* }</pre>
|
||||
*
|
||||
* @author zhouhao
|
||||
* @see Dimension
|
||||
* @since 4.0
|
||||
*/
|
||||
@Target({ElementType.METHOD, ElementType.TYPE, ElementType.FIELD})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Inherited
|
||||
@Documented
|
||||
@Dimension(type = "role", description = "控制角色")
|
||||
@Dimension(type = "role")
|
||||
@Repeatable(RequiresRoles.List.class)
|
||||
public @interface RequiresRoles {
|
||||
|
||||
/**
|
||||
* @return 角色ID
|
||||
*/
|
||||
@AliasFor(annotation = Dimension.class, attribute = "id")
|
||||
String[] value() default {};
|
||||
|
||||
/**
|
||||
* 多个角色时的判断逻辑
|
||||
* @return Logical
|
||||
*/
|
||||
@AliasFor(annotation = Dimension.class, attribute = "logical")
|
||||
Logical logical() default Logical.DEFAULT;
|
||||
|
||||
@Target({ElementType.METHOD, ElementType.TYPE, ElementType.FIELD})
|
||||
@Retention(RUNTIME)
|
||||
@Documented
|
||||
@Inherited
|
||||
@Dimension.List()
|
||||
@interface List {
|
||||
RequiresRoles[] value();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,9 @@ import org.hswebframework.web.authorization.define.Phased;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
|
||||
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||
|
||||
/**
|
||||
* 接口资源声明注解,声明Controller的资源相关信息,用于进行权限控制。
|
||||
* <br>
|
||||
@@ -53,6 +56,7 @@ import java.lang.annotation.*;
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Inherited
|
||||
@Documented
|
||||
@Repeatable(Resource.List.class)
|
||||
public @interface Resource {
|
||||
|
||||
/**
|
||||
@@ -98,4 +102,12 @@ public @interface Resource {
|
||||
* @return 是否合并
|
||||
*/
|
||||
boolean merge() default true;
|
||||
|
||||
@Target({ANNOTATION_TYPE})
|
||||
@Retention(RUNTIME)
|
||||
@Documented
|
||||
@Inherited
|
||||
@interface List {
|
||||
Resource[] value();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,9 +6,14 @@ import lombok.Setter;
|
||||
import org.hswebframework.web.authorization.DimensionType;
|
||||
import org.hswebframework.web.authorization.annotation.Logical;
|
||||
import org.hswebframework.web.bean.FastBeanCopier;
|
||||
import reactor.function.Predicate3;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.function.BiPredicate;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@@ -23,10 +28,27 @@ public class DimensionDefinition {
|
||||
|
||||
private Logical logical = Logical.DEFAULT;
|
||||
|
||||
public boolean hasDimension(Predicate3<String,Logical, Set<String>> filter) {
|
||||
return filter.test(typeId,logical, Collections.unmodifiableSet(dimensionId));
|
||||
}
|
||||
|
||||
public boolean hasDimension(Set<String> dimensionIdPredicate) {
|
||||
if (logical == Logical.AND) {
|
||||
return dimensionIdPredicate.containsAll(dimensionId);
|
||||
}
|
||||
return dimensionId
|
||||
.stream()
|
||||
.anyMatch(dimensionIdPredicate::contains);
|
||||
}
|
||||
|
||||
public boolean hasDimension(String id) {
|
||||
return dimensionId.contains(id);
|
||||
}
|
||||
|
||||
public void addDimensionI(Set<String> id) {
|
||||
dimensionId.addAll(id);
|
||||
}
|
||||
|
||||
public DimensionDefinition copy() {
|
||||
return FastBeanCopier.copy(this, DimensionDefinition::new);
|
||||
}
|
||||
|
||||
@@ -3,36 +3,64 @@ package org.hswebframework.web.authorization.define;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
import org.apache.commons.collections4.MapUtils;
|
||||
import org.apache.commons.collections4.Predicate;
|
||||
import org.hswebframework.web.authorization.Dimension;
|
||||
import org.hswebframework.web.authorization.annotation.Logical;
|
||||
import reactor.function.Predicate3;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.function.BiPredicate;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
public class DimensionsDefinition {
|
||||
|
||||
private Set<DimensionDefinition> dimensions = new HashSet<>();
|
||||
private Map<String, DimensionDefinition> dimensionsMapping = new ConcurrentHashMap<>();
|
||||
|
||||
private Logical logical = Logical.DEFAULT;
|
||||
|
||||
public void addDimension(DimensionDefinition definition) {
|
||||
dimensions.add(definition);
|
||||
private String description;
|
||||
|
||||
public Set<DimensionDefinition> getDimensions() {
|
||||
return new HashSet<>(dimensionsMapping.values());
|
||||
}
|
||||
|
||||
public boolean isEmpty(){
|
||||
return CollectionUtils.isEmpty(this.dimensions);
|
||||
public void clear() {
|
||||
dimensionsMapping.clear();
|
||||
}
|
||||
|
||||
public void addDimension(DimensionDefinition definition) {
|
||||
DimensionDefinition old = dimensionsMapping.putIfAbsent(definition.getTypeId(), definition);
|
||||
if (old != null) {
|
||||
old.addDimensionI(definition.getDimensionId());
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return MapUtils.isEmpty(this.dimensionsMapping);
|
||||
}
|
||||
|
||||
public boolean hasDimension(Dimension dimension) {
|
||||
return dimensions
|
||||
DimensionDefinition def = dimensionsMapping.get(dimension.getType().getId());
|
||||
return def != null && def.hasDimension(dimension.getId());
|
||||
}
|
||||
|
||||
public boolean hasDimension(Predicate3<String,Logical, Set<String>> filter) {
|
||||
if (logical == Logical.AND) {
|
||||
return dimensionsMapping
|
||||
.values()
|
||||
.stream()
|
||||
.anyMatch(def ->
|
||||
def.getTypeId().equals(dimension.getType().getId())
|
||||
&& def.hasDimension(dimension.getId()));
|
||||
.allMatch(e -> e.hasDimension(filter));
|
||||
} else {
|
||||
return dimensionsMapping
|
||||
.values()
|
||||
.stream()
|
||||
.anyMatch(e -> e.hasDimension(filter));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public boolean hasDimension(List<Dimension> dimensions) {
|
||||
@@ -43,4 +71,13 @@ public class DimensionsDefinition {
|
||||
|
||||
return dimensions.stream().anyMatch(this::hasDimension);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return dimensionsMapping
|
||||
.values()
|
||||
.stream()
|
||||
.map(d -> String.join(",", d.getDimensionId()) + "@" + d.getTypeId())
|
||||
.collect(Collectors.joining(";"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,11 +80,6 @@ public class ResourceDefinition implements MultipleI18nSupportEntity {
|
||||
|
||||
public synchronized ResourceDefinition addAction(ResourceActionDefinition action) {
|
||||
actionIds = null;
|
||||
ResourceActionDefinition old = getAction(action.getId()).orElse(null);
|
||||
if (old != null) {
|
||||
old.getDataAccess().getDataAccessTypes()
|
||||
.addAll(action.getDataAccess().getDataAccessTypes());
|
||||
}
|
||||
actions.add(action);
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -22,6 +22,9 @@ public class ResourcesDefinition {
|
||||
|
||||
private Phased phased = Phased.before;
|
||||
|
||||
public void clear(){
|
||||
resources.clear();
|
||||
}
|
||||
public void addResource(ResourceDefinition resource, boolean merge) {
|
||||
ResourceDefinition definition = getResource(resource.getId()).orElse(null);
|
||||
if (definition != null) {
|
||||
|
||||
@@ -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