diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/permission/core/aop/CrmPermissionAspect.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/permission/core/aop/CrmPermissionAspect.java index 292a4d664..d4902584c 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/permission/core/aop/CrmPermissionAspect.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/permission/core/aop/CrmPermissionAspect.java @@ -9,8 +9,10 @@ import cn.iocoder.yudao.module.crm.dal.dataobject.permission.CrmPermissionDO; import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum; import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum; import cn.iocoder.yudao.module.crm.framework.permission.core.annotations.CrmPermission; -import cn.iocoder.yudao.module.crm.util.CrmPermissionUtils; import cn.iocoder.yudao.module.crm.service.permission.CrmPermissionService; +import cn.iocoder.yudao.module.crm.util.CrmPermissionUtils; +import cn.iocoder.yudao.module.system.api.user.AdminUserApi; +import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; import org.aspectj.lang.JoinPoint; @@ -38,6 +40,9 @@ public class CrmPermissionAspect { @Resource private CrmPermissionService crmPermissionService; + @Resource + private AdminUserApi adminUserApi; + @Before("@annotation(crmPermission)") public void doBefore(JoinPoint joinPoint, CrmPermission crmPermission) { // 1.1 获取相关属性值 @@ -65,48 +70,75 @@ public class CrmPermissionAspect { if (CrmPermissionUtils.isCrmAdmin()) { return; } - // 1.1 没有数据权限的情况 + // 特殊:没有数据权限的情况,针对 READ 的特殊处理 if (CollUtil.isEmpty(bizPermissions)) { - // 公海数据如果没有团队成员大家也因该有读权限才对 + // 1.1 公海数据,如果没有团队成员,大家也应该有 READ 权限才对 if (CrmPermissionLevelEnum.isRead(permissionLevel)) { return; } // 没有数据权限的情况下超出了读权限直接报错,避免后面校验空指针 throw exception(CRM_PERMISSION_DENIED, CrmBizTypeEnum.getNameByType(bizType)); } else { // 1.2 有数据权限但是没有负责人的情况 - if (!anyMatch(bizPermissions, item -> CrmPermissionLevelEnum.isOwner(item.getLevel()))) { - if (CrmPermissionLevelEnum.isRead(permissionLevel)) { - return; - } + if (!anyMatch(bizPermissions, item -> CrmPermissionLevelEnum.isOwner(item.getLevel())) + && CrmPermissionLevelEnum.isRead(permissionLevel)) { + return; } } - // 2.1 情况一:如果自己是负责人,则默认有所有权限 - CrmPermissionDO userPermission = CollUtil.findOne(bizPermissions, permission -> ObjUtil.equal(permission.getUserId(), getUserId())); + // 2. 只考虑自的身权限 + Long userId = getUserId(); + CrmPermissionDO userPermission = CollUtil.findOne(bizPermissions, permission -> ObjUtil.equal(permission.getUserId(), userId)); if (userPermission != null) { - if (CrmPermissionLevelEnum.isOwner(userPermission.getLevel())) { + if (isUserPermissionValid(userPermission, permissionLevel)) { return; } - // 2.2 情况二:校验自己是否有读权限 - if (CrmPermissionLevelEnum.isRead(permissionLevel)) { - if (CrmPermissionLevelEnum.isRead(userPermission.getLevel()) // 校验当前用户是否有读权限 - || CrmPermissionLevelEnum.isWrite(userPermission.getLevel())) { // 校验当前用户是否有写权限 - return; - } - } - // 2.3 情况三:校验自己是否有写权限 - if (CrmPermissionLevelEnum.isWrite(permissionLevel)) { - if (CrmPermissionLevelEnum.isWrite(userPermission.getLevel())) { // 校验当前用户是否有写权限 - return; - } + } + + // 3. 考虑下级的权限 + List subordinateUserIds = adminUserApi.getUserListBySubordinate(userId); + for (Long subordinateUserId : convertSet(subordinateUserIds, AdminUserRespDTO::getId)) { + CrmPermissionDO subordinatePermission = CollUtil.findOne(bizPermissions, + permission -> ObjUtil.equal(permission.getUserId(), subordinateUserId)); + if (subordinatePermission != null && isUserPermissionValid(subordinatePermission, permissionLevel)) { + return; } } - // 2.4 没有权限,抛出异常 + + // 4. 没有权限,抛出异常 log.info("[doBefore][userId({}) 要求权限({}) 实际权限({}) 数据校验错误]", // 打个 info 日志,方便后续排查问题、审计 - getUserId(), permissionLevel, toJsonString(userPermission)); + userId, permissionLevel, toJsonString(userPermission)); throw exception(CRM_PERMISSION_DENIED, CrmBizTypeEnum.getNameByType(bizType)); } + /** + * 校验用户权限是否有效 + * + * @param userPermission 用户拥有的权限 + * @param permissionLevel 需要的权限级别 + * @return 是否有效 + */ + @SuppressWarnings("RedundantIfStatement") + private boolean isUserPermissionValid(CrmPermissionDO userPermission, Integer permissionLevel) { + // 2.1 情况一:如果自己是负责人,则默认有所有权限 + if (CrmPermissionLevelEnum.isOwner(userPermission.getLevel())) { + return true; + } + // 2.2 情况二:校验自己是否有读权限 + if (CrmPermissionLevelEnum.isRead(permissionLevel)) { + if (CrmPermissionLevelEnum.isRead(userPermission.getLevel()) // 校验当前用户是否有读权限 + || CrmPermissionLevelEnum.isWrite(userPermission.getLevel())) { // 校验当前用户是否有写权限 + return true; + } + } + // 2.3 情况三:校验自己是否有写权限 + if (CrmPermissionLevelEnum.isWrite(permissionLevel)) { + if (CrmPermissionLevelEnum.isWrite(userPermission.getLevel())) { // 校验当前用户是否有写权限 + return true; + } + } + return false; + } + /** * 获得用户编号 * diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/util/CrmPermissionUtils.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/util/CrmPermissionUtils.java index fa6cbb7f2..f0cd80476 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/util/CrmPermissionUtils.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/util/CrmPermissionUtils.java @@ -14,7 +14,6 @@ import com.baomidou.mybatisplus.core.toolkit.support.SFunction; import com.github.yulichang.autoconfigure.MybatisPlusJoinProperties; import com.github.yulichang.wrapper.MPJLambdaWrapper; -import java.util.Collection; import java.util.List; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; @@ -50,24 +49,21 @@ public class CrmPermissionUtils { Long userId, Integer sceneType) { MybatisPlusJoinProperties mybatisPlusJoinProperties = SpringUtil.getBean(MybatisPlusJoinProperties.class); final String ownerUserIdField = mybatisPlusJoinProperties.getTableAlias() + ".owner_user_id"; - // 1. 构建数据权限连表条件 - if (!CrmPermissionUtils.isCrmAdmin()) { // 管理员,公海不需要数据权限 - query.innerJoin(CrmPermissionDO.class, on -> on.eq(CrmPermissionDO::getBizType, bizType) - .eq(CrmPermissionDO::getBizId, bizId) // 只能使用 SFunction 如果传 id 解析出来的 sql 不对 - .eq(CrmPermissionDO::getUserId, userId)); - } - // 2.1 场景一:我负责的数据 + // 场景一:我负责的数据 if (CrmSceneTypeEnum.isOwner(sceneType)) { query.eq(ownerUserIdField, userId); } - // 2.2 场景二:我参与的数据(我有读或写权限,并且不是负责人) + // 场景二:我参与的数据(我有读或写权限,并且不是负责人) if (CrmSceneTypeEnum.isInvolved(sceneType)) { + if (CrmPermissionUtils.isCrmAdmin()) { // 特殊逻辑:如果是超管,直接查询所有,不过滤数据权限 + return; + } query.innerJoin(CrmPermissionDO.class, on -> on.eq(CrmPermissionDO::getBizType, bizType) .eq(CrmPermissionDO::getBizId, bizId) .in(CrmPermissionDO::getLevel, CrmPermissionLevelEnum.READ.getLevel(), CrmPermissionLevelEnum.WRITE.getLevel())); query.ne(ownerUserIdField, userId); } - // 2.3 场景三:下属负责的数据(下属是负责人) + // 场景三:下属负责的数据(下属是负责人) if (CrmSceneTypeEnum.isSubordinate(sceneType)) { AdminUserApi adminUserApi = SpringUtil.getBean(AdminUserApi.class); List subordinateUsers = adminUserApi.getUserListBySubordinate(userId); @@ -79,22 +75,4 @@ public class CrmPermissionUtils { } } - /** - * 构造 CRM 数据类型【批量】数据查询条件 - * - * @param query 连表查询对象 - * @param bizType 数据类型 {@link CrmBizTypeEnum} - * @param bizIds 数据编号 - * @param userId 用户编号 - */ - public static > void appendPermissionCondition(T query, - Integer bizType, Collection bizIds, Long userId) { - if (isCrmAdmin()) {// 管理员不需要数据权限 - return; - } - query.innerJoin(CrmPermissionDO.class, on -> - on.eq(CrmPermissionDO::getBizType, bizType).in(CrmPermissionDO::getBizId, bizIds) - .eq(CollUtil.isNotEmpty(bizIds), CrmPermissionDO::getUserId, userId)); - } - }