diff --git a/tyr-api/src/main/java/cn/axzo/tyr/client/feign/PermissionQueryApi.java b/tyr-api/src/main/java/cn/axzo/tyr/client/feign/PermissionQueryApi.java index 46ef850e..3cf00349 100644 --- a/tyr-api/src/main/java/cn/axzo/tyr/client/feign/PermissionQueryApi.java +++ b/tyr-api/src/main/java/cn/axzo/tyr/client/feign/PermissionQueryApi.java @@ -2,6 +2,9 @@ package cn.axzo.tyr.client.feign; import cn.axzo.framework.domain.web.result.ApiResult; import cn.axzo.tyr.client.model.req.NavTreeReq; +import cn.axzo.tyr.client.model.req.PagePermissionReq; +import cn.axzo.tyr.client.model.req.PagePermissionResp; +import cn.axzo.tyr.client.model.req.PermissionCheckReq; import cn.axzo.tyr.client.model.res.NavTreeResp; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.PostMapping; @@ -20,7 +23,15 @@ import java.util.List; @FeignClient(name = "tyr", url = "${axzo.service.tyr:http://tyr:8080}") public interface PermissionQueryApi { - /** 返回导航菜单页面 **/ + /** 返回有权限的导航菜单页面 **/ @PostMapping(value = "/api/v3/permission/query/getNavTree") ApiResult> getNavTree(@RequestBody @Valid NavTreeReq req); + + /** 页面权限详情:页面自身及所有下级 **/ + @PostMapping(value = "/api/v3/permission/query/getPagePermission") + ApiResult> getPagePermission(@RequestBody @Valid PagePermissionReq req); + + /** 鉴权接口 **/ + @PostMapping(value = "/api/v3/permission/query/hasPermission") + ApiResult hasPermission(PermissionCheckReq req); } diff --git a/tyr-api/src/main/java/cn/axzo/tyr/client/model/req/PagePermissionReq.java b/tyr-api/src/main/java/cn/axzo/tyr/client/model/req/PagePermissionReq.java new file mode 100644 index 00000000..1b639ab4 --- /dev/null +++ b/tyr-api/src/main/java/cn/axzo/tyr/client/model/req/PagePermissionReq.java @@ -0,0 +1,38 @@ +package cn.axzo.tyr.client.model.req; + +import cn.axzo.tyr.client.model.base.WorkspaceOUPair; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.NotNull; +import java.util.List; + +/** + * 页面权限查询请求 + * + * @version V1.0 + * @author: ZhanSiHu + * @date: 2024/4/9 16:09 + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class PagePermissionReq { + + @NotNull(message = "权限码不能为空") + private String featureCode; + + @NotNull(message = "人员ID不能为空") + private Long personId; + /** 登录端 **/ + @NotNull(message = "登录端不能为空") + private String terminal; + + @NotEmpty(message = "单位标识对不能为空") + private List workspaceOUPairs; + +} diff --git a/tyr-api/src/main/java/cn/axzo/tyr/client/model/req/PagePermissionResp.java b/tyr-api/src/main/java/cn/axzo/tyr/client/model/req/PagePermissionResp.java new file mode 100644 index 00000000..42a445fa --- /dev/null +++ b/tyr-api/src/main/java/cn/axzo/tyr/client/model/req/PagePermissionResp.java @@ -0,0 +1,26 @@ +package cn.axzo.tyr.client.model.req; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * 页面权限查询响应 + * + * @version V1.0 + * @author: ZhanSiHu + * @date: 2024/4/9 16:14 + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class PagePermissionResp { + + /** 权限ID **/ + private Long featureId; + /** 权限编码 **/ + private String featureCode; + +} diff --git a/tyr-api/src/main/java/cn/axzo/tyr/client/model/req/PermissionCheckReq.java b/tyr-api/src/main/java/cn/axzo/tyr/client/model/req/PermissionCheckReq.java new file mode 100644 index 00000000..c354c77a --- /dev/null +++ b/tyr-api/src/main/java/cn/axzo/tyr/client/model/req/PermissionCheckReq.java @@ -0,0 +1,39 @@ +package cn.axzo.tyr.client.model.req; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.NotNull; +import java.util.List; + +/** + * 权限校验请求 + * + * @version V1.0 + * @author: ZhanSiHu + * @date: 2024/4/9 14:17 + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class PermissionCheckReq { + + @NotNull(message = "人员ID不能为空") + private Long personId; + + @NotEmpty(message = "权限code不能为空") + private List featureCodes; + + @NotNull(message = "单位ID不能为空") + private Long ouId; + + @NotNull(message = "租户ID不能为空") + private Long workspaceId; + + /** 登录端 **/ + private String terminal; +} diff --git a/tyr-api/src/main/java/cn/axzo/tyr/client/model/req/PermissionQueryReq.java b/tyr-api/src/main/java/cn/axzo/tyr/client/model/req/PermissionQueryReq.java index f4ac67fb..113d1f12 100644 --- a/tyr-api/src/main/java/cn/axzo/tyr/client/model/req/PermissionQueryReq.java +++ b/tyr-api/src/main/java/cn/axzo/tyr/client/model/req/PermissionQueryReq.java @@ -31,8 +31,6 @@ public class PermissionQueryReq { private String terminal; - private List featureTypes; - private List featureCodes; } diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/controller/permission/PermissionQueryController.java b/tyr-server/src/main/java/cn/axzo/tyr/server/controller/permission/PermissionQueryController.java index 342f5797..2d7f22d9 100644 --- a/tyr-server/src/main/java/cn/axzo/tyr/server/controller/permission/PermissionQueryController.java +++ b/tyr-server/src/main/java/cn/axzo/tyr/server/controller/permission/PermissionQueryController.java @@ -3,6 +3,9 @@ package cn.axzo.tyr.server.controller.permission; import cn.axzo.framework.domain.web.result.ApiResult; import cn.axzo.tyr.client.feign.PermissionQueryApi; import cn.axzo.tyr.client.model.req.NavTreeReq; +import cn.axzo.tyr.client.model.req.PagePermissionReq; +import cn.axzo.tyr.client.model.req.PagePermissionResp; +import cn.axzo.tyr.client.model.req.PermissionCheckReq; import cn.axzo.tyr.client.model.res.NavTreeResp; import cn.axzo.tyr.server.service.PermissionQueryService; import lombok.RequiredArgsConstructor; @@ -29,4 +32,14 @@ public class PermissionQueryController implements PermissionQueryApi { public ApiResult> getNavTree(NavTreeReq req) { return ApiResult.ok(permissionService.getNavTree(req)); } + + @Override + public ApiResult> getPagePermission(PagePermissionReq req) { + return ApiResult.ok(permissionService.getPagePermission(req)); + } + + @Override + public ApiResult hasPermission(PermissionCheckReq req) { + return ApiResult.ok(permissionService.hasPermission(req)); + } } diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/model/PermissionQueryContext.java b/tyr-server/src/main/java/cn/axzo/tyr/server/model/PermissionQueryContext.java index 1531be19..11c793ac 100644 --- a/tyr-server/src/main/java/cn/axzo/tyr/server/model/PermissionQueryContext.java +++ b/tyr-server/src/main/java/cn/axzo/tyr/server/model/PermissionQueryContext.java @@ -8,6 +8,7 @@ import lombok.NoArgsConstructor; import javax.validation.constraints.NotEmpty; import javax.validation.constraints.NotNull; +import java.util.ArrayList; import java.util.List; import java.util.Set; @@ -37,4 +38,19 @@ public class PermissionQueryContext { /** 资源ID **/ private Set featureIds; + public PermissionQueryContext appendPersonId(Long personId) { + if (this.userIdentity == null) { + this.userIdentity = new UserIdentity(); + } + this.userIdentity.setPersonId(personId); + return this; + } + + public PermissionQueryContext appendOuWorkspace(Long ouId, Long workspaceId) { + if (this.workspaceOUPairs == null) { + this.workspaceOUPairs = new ArrayList<>(); + } + this.workspaceOUPairs.add(WorkspaceOUPair.builder().ouId(ouId).workspaceId(workspaceId).build()); + return this; + } } diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/model/ResourcePermissionQueryDTO.java b/tyr-server/src/main/java/cn/axzo/tyr/server/model/ResourcePermissionQueryDTO.java index 0b7653fd..b3818ac9 100644 --- a/tyr-server/src/main/java/cn/axzo/tyr/server/model/ResourcePermissionQueryDTO.java +++ b/tyr-server/src/main/java/cn/axzo/tyr/server/model/ResourcePermissionQueryDTO.java @@ -24,8 +24,13 @@ public class ResourcePermissionQueryDTO { private List featureTypes; + private List featureCodes; + private List terminals; private List authType; + /** 路径包含 **/ + private Long inPath; + } diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/service/PermissionQueryService.java b/tyr-server/src/main/java/cn/axzo/tyr/server/service/PermissionQueryService.java index 1d4de8ca..3beccde5 100644 --- a/tyr-server/src/main/java/cn/axzo/tyr/server/service/PermissionQueryService.java +++ b/tyr-server/src/main/java/cn/axzo/tyr/server/service/PermissionQueryService.java @@ -1,6 +1,9 @@ package cn.axzo.tyr.server.service; import cn.axzo.tyr.client.model.req.NavTreeReq; +import cn.axzo.tyr.client.model.req.PagePermissionReq; +import cn.axzo.tyr.client.model.req.PagePermissionResp; +import cn.axzo.tyr.client.model.req.PermissionCheckReq; import cn.axzo.tyr.client.model.res.NavTreeResp; import java.util.List; @@ -16,4 +19,8 @@ public interface PermissionQueryService { /** 获取导航菜单页面 **/ List getNavTree(NavTreeReq req); + + boolean hasPermission(PermissionCheckReq req); + + List getPagePermission(PagePermissionReq req); } diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/service/SaasFeatureResourceService.java b/tyr-server/src/main/java/cn/axzo/tyr/server/service/SaasFeatureResourceService.java index e9e3d214..ffa14802 100644 --- a/tyr-server/src/main/java/cn/axzo/tyr/server/service/SaasFeatureResourceService.java +++ b/tyr-server/src/main/java/cn/axzo/tyr/server/service/SaasFeatureResourceService.java @@ -35,4 +35,6 @@ public interface SaasFeatureResourceService { /** 是否免授权 **/ boolean isAuthFree(Long featureId); + + SaasFeatureResource getByCode(String featureCode); } diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/service/impl/PermissionQueryServiceImpl.java b/tyr-server/src/main/java/cn/axzo/tyr/server/service/impl/PermissionQueryServiceImpl.java index 1a7455fb..3db00c79 100644 --- a/tyr-server/src/main/java/cn/axzo/tyr/server/service/impl/PermissionQueryServiceImpl.java +++ b/tyr-server/src/main/java/cn/axzo/tyr/server/service/impl/PermissionQueryServiceImpl.java @@ -1,6 +1,7 @@ package cn.axzo.tyr.server.service.impl; import cn.axzo.basics.common.BeanMapper; +import cn.axzo.basics.common.util.AssertUtil; import cn.axzo.basics.common.util.NumberUtil; import cn.axzo.basics.common.util.TreeUtil; import cn.axzo.framework.auth.domain.TerminalInfo; @@ -9,6 +10,9 @@ import cn.axzo.tyr.client.common.enums.RoleTypeEnum; import cn.axzo.tyr.client.model.base.WorkspaceOUPair; import cn.axzo.tyr.client.model.enums.IdentityType; import cn.axzo.tyr.client.model.req.NavTreeReq; +import cn.axzo.tyr.client.model.req.PagePermissionReq; +import cn.axzo.tyr.client.model.req.PagePermissionResp; +import cn.axzo.tyr.client.model.req.PermissionCheckReq; import cn.axzo.tyr.client.model.res.NavTreeResp; import cn.axzo.tyr.server.model.PermissionDO; import cn.axzo.tyr.server.model.PermissionQueryContext; @@ -58,7 +62,12 @@ public class PermissionQueryServiceImpl implements PermissionQueryService { @Override public List getNavTree(NavTreeReq req) { //构造参数 - PermissionQueryContext context = BeanMapper.copyBean(req, PermissionQueryContext.class); + PermissionQueryContext context = PermissionQueryContext.builder() + .terminal(req.getTerminal()) + .workspaceOUPairs(req.getWorkspaceOUPairs()) + .build() + .appendPersonId(req.getPersonId()); + //查询权限 List permissions = queryUserPermission(context); @@ -75,6 +84,54 @@ public class PermissionQueryServiceImpl implements PermissionQueryService { return TreeUtil.buildTree(BeanMapper.copyList(resourceList, NavTreeResp.class)); } + @Override + public boolean hasPermission(PermissionCheckReq req) { + //权限编码转ID + List resourcePermissions = featureResourceService.permissionQuery( + ResourcePermissionQueryDTO.builder().featureCodes(req.getFeatureCodes()).build()); + if (CollectionUtil.isEmpty(resourcePermissions)) { + log.warn("no feature resource found for codes:{}", req.getFeatureCodes()); + return false; + } + PermissionQueryContext context = PermissionQueryContext.builder() + .terminal(req.getTerminal()) + .build() + .appendPersonId(req.getPersonId()) + .appendOuWorkspace(req.getOuId(), req.getWorkspaceId()); + //查询权限 + List permissions = queryUserPermission(context); + Set featureIds = permissions.stream().map(PermissionDO::getFeatureIds).flatMap(Set::stream).collect(Collectors.toSet()); + //是否任意一个有授权 + return resourcePermissions.stream().anyMatch(r -> featureIds.contains(r.getId())); + } + + @Override + public List getPagePermission(PagePermissionReq req) { + //这里没有区分是否为页面的组件或菜单树下级 同时包含了页面自身 + //权限编码转ID + SaasFeatureResource page = featureResourceService.getByCode(req.getFeatureCode()); + AssertUtil.notNull(page, "权限码不存在"); + + //所有子级 + ResourcePermissionQueryDTO param = ResourcePermissionQueryDTO.builder().inPath(page.getId()).build(); + List resourceList = featureResourceService.permissionQuery(param); + + PermissionQueryContext context = PermissionQueryContext.builder() + .terminal(req.getTerminal()) + .workspaceOUPairs(req.getWorkspaceOUPairs()) + .build() + .appendPersonId(req.getPersonId()); + //查询权限 + List permissions = queryUserPermission(context); + Set featureIds = permissions.stream().map(PermissionDO::getFeatureIds).flatMap(Set::stream).collect(Collectors.toSet()); + //权限过滤 + return resourceList.stream() + .filter(r -> featureIds.contains(r.getId())) + .map(r -> PagePermissionResp.builder() + .featureId(r.getId()) + .featureCode(r.getFeatureCode()).build()) + .collect(Collectors.toList()); + } private List queryUserPermission(PermissionQueryContext context) { diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/service/impl/SaasFeatureResourceServiceImpl.java b/tyr-server/src/main/java/cn/axzo/tyr/server/service/impl/SaasFeatureResourceServiceImpl.java index 6363050b..3429c952 100644 --- a/tyr-server/src/main/java/cn/axzo/tyr/server/service/impl/SaasFeatureResourceServiceImpl.java +++ b/tyr-server/src/main/java/cn/axzo/tyr/server/service/impl/SaasFeatureResourceServiceImpl.java @@ -75,7 +75,9 @@ public class SaasFeatureResourceServiceImpl implements SaasFeatureResourceServic SaasFeatureResource::getAuthType) .in(CollectionUtil.isNotEmpty(param.getIds()), SaasFeatureResource::getId, param.getIds()) .in(CollectionUtil.isNotEmpty(param.getFeatureTypes()), SaasFeatureResource::getFeatureType, param.getFeatureTypes()) + .in(CollectionUtil.isNotEmpty(param.getFeatureCodes()), SaasFeatureResource::getFeatureCode, param.getFeatureCodes()) .in(CollectionUtil.isNotEmpty(param.getTerminals()), SaasFeatureResource::getTerminal, param.getTerminals()) + .apply(Objects.nonNull(param.getInPath()), " FIND_IN_SET(" + param.getInPath() + ", path)") .list(); return BeanMapper.copyList(resourceList, ResourcePermission.class); } @@ -100,6 +102,11 @@ public class SaasFeatureResourceServiceImpl implements SaasFeatureResourceServic return RedisClient.SetOps.sIsMember(KEY_AUTH_FREE, featureId); } + @Override + public SaasFeatureResource getByCode(String featureCode) { + return featureResourceDao.getByCode(featureCode); + } + @Override public void saveOrUpdateMenu(FeatureResourceTreeSaveReq req) { SaasFeatureResource baseResource = BeanMapper.copyBean(req, SaasFeatureResource.class);