Browse Source

增加权限过滤:赋权时防止越权。

zhou-hao 4 năm trước cách đây
mục cha
commit
d84f08c617

+ 16 - 0
hsweb-system/hsweb-system-authorization/hsweb-system-authorization-api/src/main/java/org/hswebframework/web/system/authorization/api/entity/AuthorizationSettingEntity.java

@@ -8,7 +8,9 @@ import org.hswebframework.ezorm.rdb.mapping.annotation.Comment;
 import org.hswebframework.ezorm.rdb.mapping.annotation.DefaultValue;
 import org.hswebframework.ezorm.rdb.mapping.annotation.JsonCodec;
 import org.hswebframework.web.api.crud.entity.Entity;
+import org.hswebframework.web.bean.FastBeanCopier;
 import org.hswebframework.web.validator.CreateGroup;
+import org.springframework.util.CollectionUtils;
 
 import javax.persistence.*;
 import javax.validation.constraints.NotBlank;
@@ -16,6 +18,8 @@ import javax.validation.constraints.NotNull;
 import java.sql.JDBCType;
 import java.util.List;
 import java.util.Set;
+import java.util.function.Predicate;
+import java.util.stream.Collectors;
 
 @Table(name = "s_autz_setting_info", indexes = {
         @Index(name = "idx_sasi_dss", columnList = "dimension_type,dimension_target,state desc"),
@@ -86,4 +90,16 @@ public class AuthorizationSettingEntity implements Entity {
     @Comment("是否合并")
     @Schema(description = "冲突时,是否合并")
     private Boolean merge;
+
+    public AuthorizationSettingEntity copy(Predicate<String> actionFilter,
+                                           Predicate<DataAccessEntity> dataAccessFilter){
+        AuthorizationSettingEntity newSetting= FastBeanCopier.copy(this,new AuthorizationSettingEntity());
+        if(!CollectionUtils.isEmpty(newSetting.getActions())){
+            newSetting.setActions(newSetting.getActions().stream().filter(actionFilter).collect(Collectors.toSet()));
+        }
+        if(!CollectionUtils.isEmpty(newSetting.getDataAccesses())){
+            newSetting.setDataAccesses(newSetting.getDataAccesses().stream().filter(dataAccessFilter).collect(Collectors.toList()));
+        }
+        return newSetting;
+    }
 }

+ 22 - 1
hsweb-system/hsweb-system-authorization/hsweb-system-authorization-api/src/main/java/org/hswebframework/web/system/authorization/api/entity/PermissionEntity.java

@@ -7,7 +7,9 @@ import org.hswebframework.ezorm.rdb.mapping.annotation.Comment;
 import org.hswebframework.ezorm.rdb.mapping.annotation.DefaultValue;
 import org.hswebframework.ezorm.rdb.mapping.annotation.JsonCodec;
 import org.hswebframework.web.api.crud.entity.GenericEntity;
+import org.hswebframework.web.bean.FastBeanCopier;
 import org.hswebframework.web.validator.CreateGroup;
+import org.springframework.util.CollectionUtils;
 
 import javax.persistence.Column;
 import javax.persistence.Table;
@@ -16,6 +18,9 @@ import javax.validation.constraints.NotNull;
 import java.sql.JDBCType;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
+import java.util.function.Predicate;
+import java.util.stream.Collectors;
 
 @Table(name = "s_permission")
 @Getter
@@ -28,7 +33,7 @@ public class PermissionEntity extends GenericEntity<String> {
     @Column
     @Comment("权限名称")
     @Schema(description = "权限名称")
-    @NotBlank(message = "权限名称不能为空",groups = CreateGroup.class)
+    @NotBlank(message = "权限名称不能为空", groups = CreateGroup.class)
     private String name;
 
     @Column
@@ -70,4 +75,20 @@ public class PermissionEntity extends GenericEntity<String> {
     @Schema(description = "其他配置")
     private Map<String, Object> properties;
 
+    public PermissionEntity copy(Predicate<ActionEntity> actionFilter,
+                                 Predicate<OptionalField> fieldFilter) {
+        PermissionEntity entity = FastBeanCopier.copy(this, new PermissionEntity());
+
+        if (!CollectionUtils.isEmpty(entity.getActions())) {
+            entity.setActions(entity.getActions().stream().filter(actionFilter).collect(Collectors.toList()));
+        }
+        if (!CollectionUtils.isEmpty(entity.getOptionalFields())) {
+            entity.setOptionalFields(entity
+                                             .getOptionalFields()
+                                             .stream()
+                                             .filter(fieldFilter)
+                                             .collect(Collectors.toList()));
+        }
+        return entity;
+    }
 }

+ 4 - 0
hsweb-system/hsweb-system-authorization/hsweb-system-authorization-default/src/main/java/org/hswebframework/web/system/authorization/defaults/configuration/AuthorizationServiceAutoConfiguration.java

@@ -76,4 +76,8 @@ public class AuthorizationServiceAutoConfiguration {
         return new UserDimensionTerm();
     }
 
+    @Bean
+    public PermissionProperties permissionProperties(){
+        return new PermissionProperties();
+    }
 }

+ 89 - 0
hsweb-system/hsweb-system-authorization/hsweb-system-authorization-default/src/main/java/org/hswebframework/web/system/authorization/defaults/configuration/PermissionProperties.java

@@ -0,0 +1,89 @@
+package org.hswebframework.web.system.authorization.defaults.configuration;
+
+import lombok.Getter;
+import lombok.Setter;
+import org.hswebframework.web.authorization.Authentication;
+import org.hswebframework.web.authorization.Permission;
+import org.hswebframework.web.authorization.exception.AccessDenyException;
+import org.hswebframework.web.system.authorization.api.entity.AuthorizationSettingEntity;
+import org.hswebframework.web.system.authorization.api.entity.PermissionEntity;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.util.CollectionUtils;
+import reactor.core.publisher.Flux;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+@Getter
+@Setter
+@ConfigurationProperties(prefix = "hsweb.permission")
+public class PermissionProperties {
+
+    private PermissionFilter filter = new PermissionFilter();
+
+    @Getter
+    @Setter
+    public static class PermissionFilter {
+        //开启权限过滤
+        private boolean enabled = false;
+        //越权赋权时处理逻辑
+        private UnAuthStrategy unAuthStrategy = UnAuthStrategy.error;
+
+        private Set<String> excludeUsername = new HashSet<>();
+
+        public AuthorizationSettingEntity handleSetting(Authentication authentication,
+                                                        AuthorizationSettingEntity setting) {
+            if (!enabled || excludeUsername.contains(authentication.getUser().getUsername())) {
+                return setting;
+            }
+            //有全部权限
+            if (authentication.hasPermission(setting.getPermission(), setting.getActions())) {
+                return setting;
+            }
+            //交给具体的策略处理
+            return unAuthStrategy.handle(authentication, setting);
+        }
+
+        public Flux<PermissionEntity> doFilter(Flux<PermissionEntity> flux, Authentication authentication) {
+            if (!enabled || excludeUsername.contains(authentication.getUser().getUsername())) {
+                return flux;
+            }
+            return flux
+                    .map(entity -> entity
+                            .copy(action -> authentication.hasPermission(entity.getId(), action.getAction()),
+                                  optionalField -> true))
+                    .filter(entity -> !CollectionUtils.isEmpty(entity.getActions()));
+        }
+
+        public enum UnAuthStrategy {
+            //忽略赋权
+            ignore {
+                @Override
+                public AuthorizationSettingEntity handle(Authentication authentication, AuthorizationSettingEntity setting) {
+
+                    return setting.copy(action -> authentication.hasPermission(setting.getPermission(), action), access -> true);
+                }
+            },
+            //抛出错误
+            error {
+                @Override
+                public AuthorizationSettingEntity handle(Authentication authentication, AuthorizationSettingEntity setting) {
+                    if (!authentication.hasPermission(setting.getPermission())) {
+                        throw new AccessDenyException("当前用户无权限:" + setting.getPermission());
+                    }
+                    Set<String> actions = new HashSet<>(setting.getActions());
+                    actions.removeAll(authentication
+                                              .getPermission(setting.getPermission())
+                                              .map(Permission::getActions)
+                                              .orElseGet(Collections::emptySet));
+
+                    throw new AccessDenyException("当前用户无权限:" + setting.getPermission() + "" +actions);
+                }
+            };
+
+            public abstract AuthorizationSettingEntity handle(Authentication authentication,
+                                                              AuthorizationSettingEntity setting);
+        }
+    }
+}

+ 1 - 0
hsweb-system/hsweb-system-authorization/hsweb-system-authorization-default/src/main/java/org/hswebframework/web/system/authorization/defaults/service/DefaultAuthorizationSettingService.java

@@ -9,6 +9,7 @@ import org.hswebframework.web.authorization.DimensionType;
 import org.hswebframework.web.crud.service.GenericReactiveCrudService;
 import org.hswebframework.web.system.authorization.api.entity.AuthorizationSettingEntity;
 import org.hswebframework.web.system.authorization.api.event.ClearUserAuthorizationCacheEvent;
+import org.hswebframework.web.system.authorization.defaults.configuration.PermissionProperties;
 import org.reactivestreams.Publisher;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.context.ApplicationEventPublisher;

+ 20 - 1
hsweb-system/hsweb-system-authorization/hsweb-system-authorization-default/src/main/java/org/hswebframework/web/system/authorization/defaults/webflux/WebFluxAuthorizationSettingController.java

@@ -2,28 +2,47 @@ package org.hswebframework.web.system.authorization.defaults.webflux;
 
 
 import io.swagger.v3.oas.annotations.tags.Tag;
+import org.hswebframework.ezorm.rdb.mapping.defaults.SaveResult;
+import org.hswebframework.web.authorization.Authentication;
 import org.hswebframework.web.authorization.annotation.Authorize;
 import org.hswebframework.web.authorization.annotation.Resource;
 import org.hswebframework.web.crud.service.ReactiveCrudService;
 import org.hswebframework.web.crud.web.reactive.ReactiveServiceCrudController;
 import org.hswebframework.web.system.authorization.api.entity.AuthorizationSettingEntity;
+import org.hswebframework.web.system.authorization.defaults.configuration.PermissionProperties;
 import org.hswebframework.web.system.authorization.defaults.service.DefaultAuthorizationSettingService;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.RequestBody;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RestController;
+import reactor.core.publisher.Flux;
+import reactor.core.publisher.Mono;
 
 @RestController
 @RequestMapping("/autz-setting")
 @Authorize
-@Resource(id = "autz-setting",name = "权限分配",group = "system")
+@Resource(id = "autz-setting", name = "权限分配", group = "system")
 @Tag(name = "权限分配")
 public class WebFluxAuthorizationSettingController implements ReactiveServiceCrudController<AuthorizationSettingEntity, String> {
 
     @Autowired
     private DefaultAuthorizationSettingService settingService;
 
+    @Autowired
+    private PermissionProperties permissionProperties;
+
     @Override
     public ReactiveCrudService<AuthorizationSettingEntity, String> getService() {
         return settingService;
     }
+
+    @Override
+    public AuthorizationSettingEntity applyAuthentication(AuthorizationSettingEntity entity,
+                                                          Authentication authentication) {
+        AuthorizationSettingEntity setting = ReactiveServiceCrudController.super.applyAuthentication(entity, authentication);
+
+        return permissionProperties
+                .getFilter()
+                .handleSetting(authentication, setting);
+    }
 }

+ 33 - 12
hsweb-system/hsweb-system-authorization/hsweb-system-authorization-default/src/main/java/org/hswebframework/web/system/authorization/defaults/webflux/WebFluxPermissionController.java

@@ -4,15 +4,20 @@ import io.swagger.v3.oas.annotations.Operation;
 import io.swagger.v3.oas.annotations.Parameter;
 import io.swagger.v3.oas.annotations.tags.Tag;
 import org.apache.commons.collections.CollectionUtils;
-import org.hswebframework.web.authorization.annotation.Authorize;
-import org.hswebframework.web.authorization.annotation.QueryAction;
-import org.hswebframework.web.authorization.annotation.Resource;
+import org.hswebframework.ezorm.rdb.operator.dml.query.SortOrder;
+import org.hswebframework.web.api.crud.entity.QueryNoPagingOperation;
+import org.hswebframework.web.api.crud.entity.QueryOperation;
+import org.hswebframework.web.api.crud.entity.QueryParamEntity;
+import org.hswebframework.web.authorization.Authentication;
+import org.hswebframework.web.authorization.annotation.*;
 import org.hswebframework.web.crud.service.ReactiveCrudService;
 import org.hswebframework.web.crud.web.reactive.ReactiveServiceCrudController;
 import org.hswebframework.web.system.authorization.api.entity.PermissionEntity;
+import org.hswebframework.web.system.authorization.defaults.configuration.PermissionProperties;
 import org.hswebframework.web.system.authorization.defaults.service.DefaultPermissionService;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.*;
+import reactor.core.publisher.Flux;
 import reactor.core.publisher.Mono;
 
 import java.util.List;
@@ -27,24 +32,40 @@ public class WebFluxPermissionController implements ReactiveServiceCrudControlle
     @Autowired
     private DefaultPermissionService permissionService;
 
+    @Autowired
+    private PermissionProperties permissionProperties;
+
     @Override
     public ReactiveCrudService<PermissionEntity, String> getService() {
         return permissionService;
     }
 
     @PutMapping("/status/{status}")
-    @QueryAction
+    @SaveAction
     @Operation(summary = "批量修改权限状态")
-    public Mono<Integer> changePermissionState(@PathVariable @Parameter(description = "状态值:0禁用,1启用.") Byte status, @RequestBody List<String> idList) {
+    public Mono<Integer> changePermissionState(@PathVariable @Parameter(description = "状态值:0禁用,1启用.") Byte status,
+                                               @RequestBody List<String> idList) {
 
         return Mono.just(idList)
-                .filter(CollectionUtils::isNotEmpty)
-                .flatMap(list -> permissionService.createUpdate()
-                        .set(PermissionEntity::getStatus, status)
-                        .where()
-                        .in(PermissionEntity::getId, list)
-                        .execute())
-                .defaultIfEmpty(0);
+                   .filter(CollectionUtils::isNotEmpty)
+                   .flatMap(list -> permissionService
+                           .createUpdate()
+                           .set(PermissionEntity::getStatus, status)
+                           .where()
+                           .in(PermissionEntity::getId, list)
+                           .execute())
+                   .defaultIfEmpty(0);
+
+    }
 
+    @GetMapping("/_query/for-grant")
+    @ResourceAction(id = "grant", name = "赋权")
+    @QueryNoPagingOperation(summary = "获取用于赋权的权限列表")
+    public Flux<PermissionEntity> queryForGrant(QueryParamEntity query) {
+        return Authentication
+                .currentReactive()
+                .flatMapMany(auth -> permissionProperties
+                        .getFilter()
+                        .doFilter(permissionService.query(query.noPaging()), auth));
     }
 }