|
@@ -1,31 +1,37 @@
|
|
|
package org.jetlinks.community.auth.web;
|
|
|
|
|
|
-import io.swagger.v3.oas.annotations.Hidden;
|
|
|
+import io.swagger.v3.oas.annotations.Operation;
|
|
|
+import io.swagger.v3.oas.annotations.tags.Tag;
|
|
|
+import lombok.AllArgsConstructor;
|
|
|
import org.hswebframework.web.api.crud.entity.TreeSupportEntity;
|
|
|
import org.hswebframework.web.authorization.Authentication;
|
|
|
-import org.hswebframework.web.authorization.AuthenticationUtils;
|
|
|
import org.hswebframework.web.authorization.annotation.Authorize;
|
|
|
import org.hswebframework.web.authorization.annotation.Resource;
|
|
|
+import org.hswebframework.web.authorization.annotation.ResourceAction;
|
|
|
import org.hswebframework.web.authorization.exception.UnAuthorizedException;
|
|
|
import org.hswebframework.web.crud.service.ReactiveCrudService;
|
|
|
import org.hswebframework.web.crud.web.reactive.ReactiveServiceCrudController;
|
|
|
+import org.jetlinks.community.auth.entity.MenuButtonInfo;
|
|
|
import org.jetlinks.community.auth.entity.MenuEntity;
|
|
|
+import org.jetlinks.community.auth.entity.MenuView;
|
|
|
+import org.jetlinks.community.auth.service.AuthorizationSettingDetailService;
|
|
|
import org.jetlinks.community.auth.service.DefaultMenuService;
|
|
|
-import org.springframework.beans.factory.annotation.Autowired;
|
|
|
+import org.jetlinks.community.auth.web.request.MenuGrantRequest;
|
|
|
import org.springframework.util.StringUtils;
|
|
|
-import org.springframework.web.bind.annotation.GetMapping;
|
|
|
-import org.springframework.web.bind.annotation.RequestMapping;
|
|
|
-import org.springframework.web.bind.annotation.RestController;
|
|
|
+import org.springframework.web.bind.annotation.*;
|
|
|
import reactor.core.publisher.Flux;
|
|
|
import reactor.core.publisher.Mono;
|
|
|
|
|
|
-import java.util.*;
|
|
|
+import java.util.Collection;
|
|
|
+import java.util.HashMap;
|
|
|
+import java.util.Map;
|
|
|
import java.util.function.Function;
|
|
|
import java.util.function.Predicate;
|
|
|
import java.util.stream.Collectors;
|
|
|
|
|
|
/**
|
|
|
* 菜单管理
|
|
|
+ *
|
|
|
* @author wangzheng
|
|
|
* @since 1.0
|
|
|
*/
|
|
@@ -33,27 +39,140 @@ import java.util.stream.Collectors;
|
|
|
@RequestMapping("/menu")
|
|
|
@Authorize
|
|
|
@Resource(id = "menu", name = "菜单管理", group = "system")
|
|
|
-@Hidden
|
|
|
+@Tag(name = "菜单管理")
|
|
|
+@AllArgsConstructor
|
|
|
public class MenuController implements ReactiveServiceCrudController<MenuEntity, String> {
|
|
|
|
|
|
- @Autowired
|
|
|
- private DefaultMenuService defaultMenuService;
|
|
|
+ private final DefaultMenuService defaultMenuService;
|
|
|
+
|
|
|
+ private final AuthorizationSettingDetailService settingService;
|
|
|
|
|
|
@Override
|
|
|
public ReactiveCrudService<MenuEntity, String> getService() {
|
|
|
return defaultMenuService;
|
|
|
}
|
|
|
|
|
|
- public Collection<MenuEntity> predicateUserMenu(Map<String, MenuEntity> menuMap, Authentication autz) {
|
|
|
+ /**
|
|
|
+ * 获取用户自己的菜单列表
|
|
|
+ *
|
|
|
+ * @return 菜单列表
|
|
|
+ */
|
|
|
+ @GetMapping("/user-own/tree")
|
|
|
+ @Authorize(merge = false)
|
|
|
+ @Operation(summary = "获取当前用户可访问的菜单(树结构)")
|
|
|
+ public Flux<MenuView> getUserMenuAsTree() {
|
|
|
+ return this
|
|
|
+ .getUserMenuAsList()
|
|
|
+ .as(MenuController::listToTree);
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ @GetMapping("/user-own/list")
|
|
|
+ @Authorize(merge = false)
|
|
|
+ @Operation(summary = "获取当前用户可访问的菜单(列表结构)")
|
|
|
+ public Flux<MenuView> getUserMenuAsList() {
|
|
|
+ return Authentication
|
|
|
+ .currentReactive()
|
|
|
+ .switchIfEmpty(Mono.error(UnAuthorizedException::new))
|
|
|
+ .flatMapMany(autz -> defaultMenuService
|
|
|
+ .createQuery()
|
|
|
+ .where(MenuEntity::getStatus,1)
|
|
|
+ .fetch()
|
|
|
+ .collect(Collectors.toMap(MenuEntity::getId, Function.identity()))
|
|
|
+ .flatMapIterable(menuMap -> MenuController
|
|
|
+ .convertMenuView(menuMap,
|
|
|
+ menu -> "admin".equals(autz.getUser().getUsername()) ||
|
|
|
+ menu.hasPermission(autz::hasPermission),
|
|
|
+ button -> "admin".equals(autz.getUser().getUsername()) ||
|
|
|
+ button.hasPermission(autz::hasPermission)
|
|
|
+ )));
|
|
|
+ }
|
|
|
+
|
|
|
+ @PutMapping("/_grant")
|
|
|
+ @Operation(summary = "根据菜单进行授权")
|
|
|
+ @ResourceAction(id = "grant", name = "授权")
|
|
|
+ public Mono<Void> grant(@RequestBody Mono<MenuGrantRequest> body) {
|
|
|
+ return Mono
|
|
|
+ .zip(
|
|
|
+ //T1: 当前用户权限信息
|
|
|
+ Authentication.currentReactive(),
|
|
|
+ //T2: 将菜单信息转为授权信息
|
|
|
+ Mono
|
|
|
+ .zip(body,
|
|
|
+ defaultMenuService
|
|
|
+ .createQuery()
|
|
|
+ .where(MenuEntity::getStatus,1)
|
|
|
+ .fetch()
|
|
|
+ .collectList(),
|
|
|
+ MenuGrantRequest::toAuthorizationSettingDetail
|
|
|
+ )
|
|
|
+ .map(Flux::just),
|
|
|
+ //保存授权信息
|
|
|
+ settingService::saveDetail
|
|
|
+ )
|
|
|
+ .flatMap(Function.identity());
|
|
|
+ }
|
|
|
+
|
|
|
+ @GetMapping("/{targetType}/{targetId}/_grant/tree")
|
|
|
+ @ResourceAction(id = "grant", name = "授权")
|
|
|
+ @Operation(summary = "获取菜单授权信息(树结构)")
|
|
|
+ public Flux<MenuView> getGrantInfoTree(@PathVariable String targetType,
|
|
|
+ @PathVariable String targetId) {
|
|
|
+
|
|
|
+ return this
|
|
|
+ .getGrantInfo(targetType, targetId)
|
|
|
+ .as(MenuController::listToTree);
|
|
|
+ }
|
|
|
+
|
|
|
+ @GetMapping("/{targetType}/{targetId}/_grant/list")
|
|
|
+ @ResourceAction(id = "grant", name = "授权")
|
|
|
+ @Operation(summary = "获取菜单授权信息(列表结构)")
|
|
|
+ public Flux<MenuView> getGrantInfo(@PathVariable String targetType,
|
|
|
+ @PathVariable String targetId) {
|
|
|
+
|
|
|
+ return Mono
|
|
|
+ .zip(
|
|
|
+ //权限设置信息
|
|
|
+ settingService.getSettingDetail(targetType, targetId),
|
|
|
+ //菜单
|
|
|
+ defaultMenuService
|
|
|
+ .createQuery()
|
|
|
+ .where(MenuEntity::getStatus,1)
|
|
|
+ .fetch()
|
|
|
+ .collectMap(MenuEntity::getId, Function.identity()),
|
|
|
+ (detail, menuMap) -> MenuController
|
|
|
+ .convertMenuView(menuMap,
|
|
|
+ menu -> menu.hasPermission(detail::hasPermission),
|
|
|
+ button -> button.hasPermission(detail::hasPermission)
|
|
|
+ )
|
|
|
+ )
|
|
|
+ .flatMapIterable(Function.identity());
|
|
|
+ }
|
|
|
+
|
|
|
+ private static Flux<MenuView> listToTree(Flux<MenuView> flux) {
|
|
|
+ return flux
|
|
|
+ .collectList()
|
|
|
+ .flatMapIterable(list -> TreeSupportEntity
|
|
|
+ .list2tree(list,
|
|
|
+ MenuView::setChildren,
|
|
|
+ (Predicate<MenuView>) n ->
|
|
|
+ StringUtils.isEmpty(n.getParentId())
|
|
|
+ || "-1".equals(n.getParentId())));
|
|
|
+ }
|
|
|
+
|
|
|
+ private static Collection<MenuView> convertMenuView(Map<String, MenuEntity> menuMap,
|
|
|
+ Predicate<MenuEntity> menuPredicate,
|
|
|
+ Predicate<MenuButtonInfo> buttonPredicate) {
|
|
|
Map<String, MenuEntity> group = new HashMap<>();
|
|
|
for (MenuEntity menu : menuMap.values()) {
|
|
|
if (group.containsKey(menu.getId())) {
|
|
|
continue;
|
|
|
}
|
|
|
- if (autz.getUser().getUsername().equals("admin") || AuthenticationUtils.createPredicate(menu.getPermissionExpression()).test(autz)) {
|
|
|
+ if (menuPredicate.test(menu)) {
|
|
|
String parentId = menu.getParentId();
|
|
|
MenuEntity parent;
|
|
|
group.put(menu.getId(), menu);
|
|
|
+ //有子菜单默认就有父菜单
|
|
|
while (!StringUtils.isEmpty(parentId)) {
|
|
|
parent = menuMap.get(parentId);
|
|
|
if (parent == null) {
|
|
@@ -64,33 +183,12 @@ public class MenuController implements ReactiveServiceCrudController<MenuEntity,
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
- List<MenuEntity> list = new ArrayList<>(group.values());
|
|
|
- Collections.sort(list);
|
|
|
-
|
|
|
- return list;
|
|
|
+ return group
|
|
|
+ .values()
|
|
|
+ .stream()
|
|
|
+ .map(menu -> MenuView.of(menu.copy(buttonPredicate)))
|
|
|
+ .sorted()
|
|
|
+ .collect(Collectors.toList());
|
|
|
}
|
|
|
|
|
|
- /**
|
|
|
- * 获取用户自己的菜单列表
|
|
|
- * @return 菜单列表
|
|
|
- */
|
|
|
- @GetMapping("user-own/tree")
|
|
|
- @Authorize(merge = false)
|
|
|
- public Flux<MenuEntity> getUserMenuAsTree() {
|
|
|
- return Authentication
|
|
|
- .currentReactive()
|
|
|
- .switchIfEmpty(Mono.error(UnAuthorizedException::new))
|
|
|
- .flatMapMany(autz -> defaultMenuService
|
|
|
- .createQuery()
|
|
|
- .fetch()
|
|
|
- .collect(Collectors.toMap(MenuEntity::getId, Function.identity()))
|
|
|
- .map(menuMap -> predicateUserMenu(menuMap, autz))
|
|
|
- .map(menus -> TreeSupportEntity.list2tree(
|
|
|
- menus,
|
|
|
- MenuEntity::setChildren,
|
|
|
- (Predicate<MenuEntity>) n ->
|
|
|
- StringUtils.isEmpty(n.getParentId())
|
|
|
- || "-1".equals(n.getParentId()))).flatMapMany(Flux::fromIterable));
|
|
|
-
|
|
|
- }
|
|
|
}
|