Commit d50844246cd44a77380d59c88c46126875c368e9

Authored by YunaiV
1 parent 6269f050

【缺陷修复】CRM:上级无法读取下级的数据问题

① CrmPermissionUtils 解决分页过滤
② CrmPermissionAspect 解决查询详情过滤
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;
9 9 import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;
10 10 import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum;
11 11 import cn.iocoder.yudao.module.crm.framework.permission.core.annotations.CrmPermission;
12   -import cn.iocoder.yudao.module.crm.util.CrmPermissionUtils;
13 12 import cn.iocoder.yudao.module.crm.service.permission.CrmPermissionService;
  13 +import cn.iocoder.yudao.module.crm.util.CrmPermissionUtils;
  14 +import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
  15 +import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
14 16 import jakarta.annotation.Resource;
15 17 import lombok.extern.slf4j.Slf4j;
16 18 import org.aspectj.lang.JoinPoint;
... ... @@ -38,6 +40,9 @@ public class CrmPermissionAspect {
38 40 @Resource
39 41 private CrmPermissionService crmPermissionService;
40 42  
  43 + @Resource
  44 + private AdminUserApi adminUserApi;
  45 +
41 46 @Before("@annotation(crmPermission)")
42 47 public void doBefore(JoinPoint joinPoint, CrmPermission crmPermission) {
43 48 // 1.1 获取相关属性值
... ... @@ -65,49 +70,76 @@ public class CrmPermissionAspect {
65 70 if (CrmPermissionUtils.isCrmAdmin()) {
66 71 return;
67 72 }
68   - // 1.1 没有数据权限的情况
  73 + // 特殊:没有数据权限的情况,针对 READ 的特殊处理
69 74 if (CollUtil.isEmpty(bizPermissions)) {
70   - // 公海数据如果没有团队成员大家也因该有读权限才对
  75 + // 1.1 公海数据,如果没有团队成员,大家也应该有 READ 权限才对
71 76 if (CrmPermissionLevelEnum.isRead(permissionLevel)) {
72 77 return;
73 78 }
74 79 // 没有数据权限的情况下超出了读权限直接报错,避免后面校验空指针
75 80 throw exception(CRM_PERMISSION_DENIED, CrmBizTypeEnum.getNameByType(bizType));
76 81 } else { // 1.2 有数据权限但是没有负责人的情况
77   - if (!anyMatch(bizPermissions, item -> CrmPermissionLevelEnum.isOwner(item.getLevel()))) {
78   - if (CrmPermissionLevelEnum.isRead(permissionLevel)) {
79   - return;
80   - }
  82 + if (!anyMatch(bizPermissions, item -> CrmPermissionLevelEnum.isOwner(item.getLevel()))
  83 + && CrmPermissionLevelEnum.isRead(permissionLevel)) {
  84 + return;
81 85 }
82 86 }
83 87  
84   - // 2.1 情况一:如果自己是负责人,则默认有所有权限
85   - CrmPermissionDO userPermission = CollUtil.findOne(bizPermissions, permission -> ObjUtil.equal(permission.getUserId(), getUserId()));
  88 + // 2. 只考虑自的身权限
  89 + Long userId = getUserId();
  90 + CrmPermissionDO userPermission = CollUtil.findOne(bizPermissions, permission -> ObjUtil.equal(permission.getUserId(), userId));
86 91 if (userPermission != null) {
87   - if (CrmPermissionLevelEnum.isOwner(userPermission.getLevel())) {
  92 + if (isUserPermissionValid(userPermission, permissionLevel)) {
88 93 return;
89 94 }
90   - // 2.2 情况二:校验自己是否有读权限
91   - if (CrmPermissionLevelEnum.isRead(permissionLevel)) {
92   - if (CrmPermissionLevelEnum.isRead(userPermission.getLevel()) // 校验当前用户是否有读权限
93   - || CrmPermissionLevelEnum.isWrite(userPermission.getLevel())) { // 校验当前用户是否有写权限
94   - return;
95   - }
96   - }
97   - // 2.3 情况三:校验自己是否有写权限
98   - if (CrmPermissionLevelEnum.isWrite(permissionLevel)) {
99   - if (CrmPermissionLevelEnum.isWrite(userPermission.getLevel())) { // 校验当前用户是否有写权限
100   - return;
101   - }
  95 + }
  96 +
  97 + // 3. 考虑下级的权限
  98 + List<AdminUserRespDTO> subordinateUserIds = adminUserApi.getUserListBySubordinate(userId);
  99 + for (Long subordinateUserId : convertSet(subordinateUserIds, AdminUserRespDTO::getId)) {
  100 + CrmPermissionDO subordinatePermission = CollUtil.findOne(bizPermissions,
  101 + permission -> ObjUtil.equal(permission.getUserId(), subordinateUserId));
  102 + if (subordinatePermission != null && isUserPermissionValid(subordinatePermission, permissionLevel)) {
  103 + return;
102 104 }
103 105 }
104   - // 2.4 没有权限,抛出异常
  106 +
  107 + // 4. 没有权限,抛出异常
105 108 log.info("[doBefore][userId({}) 要求权限({}) 实际权限({}) 数据校验错误]", // 打个 info 日志,方便后续排查问题、审计
106   - getUserId(), permissionLevel, toJsonString(userPermission));
  109 + userId, permissionLevel, toJsonString(userPermission));
107 110 throw exception(CRM_PERMISSION_DENIED, CrmBizTypeEnum.getNameByType(bizType));
108 111 }
109 112  
110 113 /**
  114 + * 校验用户权限是否有效
  115 + *
  116 + * @param userPermission 用户拥有的权限
  117 + * @param permissionLevel 需要的权限级别
  118 + * @return 是否有效
  119 + */
  120 + @SuppressWarnings("RedundantIfStatement")
  121 + private boolean isUserPermissionValid(CrmPermissionDO userPermission, Integer permissionLevel) {
  122 + // 2.1 情况一:如果自己是负责人,则默认有所有权限
  123 + if (CrmPermissionLevelEnum.isOwner(userPermission.getLevel())) {
  124 + return true;
  125 + }
  126 + // 2.2 情况二:校验自己是否有读权限
  127 + if (CrmPermissionLevelEnum.isRead(permissionLevel)) {
  128 + if (CrmPermissionLevelEnum.isRead(userPermission.getLevel()) // 校验当前用户是否有读权限
  129 + || CrmPermissionLevelEnum.isWrite(userPermission.getLevel())) { // 校验当前用户是否有写权限
  130 + return true;
  131 + }
  132 + }
  133 + // 2.3 情况三:校验自己是否有写权限
  134 + if (CrmPermissionLevelEnum.isWrite(permissionLevel)) {
  135 + if (CrmPermissionLevelEnum.isWrite(userPermission.getLevel())) { // 校验当前用户是否有写权限
  136 + return true;
  137 + }
  138 + }
  139 + return false;
  140 + }
  141 +
  142 + /**
111 143 * 获得用户编号
112 144 *
113 145 * @return 用户编号
... ...
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;
14 14 import com.github.yulichang.autoconfigure.MybatisPlusJoinProperties;
15 15 import com.github.yulichang.wrapper.MPJLambdaWrapper;
16 16  
17   -import java.util.Collection;
18 17 import java.util.List;
19 18  
20 19 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
... ... @@ -50,24 +49,21 @@ public class CrmPermissionUtils {
50 49 Long userId, Integer sceneType) {
51 50 MybatisPlusJoinProperties mybatisPlusJoinProperties = SpringUtil.getBean(MybatisPlusJoinProperties.class);
52 51 final String ownerUserIdField = mybatisPlusJoinProperties.getTableAlias() + ".owner_user_id";
53   - // 1. 构建数据权限连表条件
54   - if (!CrmPermissionUtils.isCrmAdmin()) { // 管理员,公海不需要数据权限
55   - query.innerJoin(CrmPermissionDO.class, on -> on.eq(CrmPermissionDO::getBizType, bizType)
56   - .eq(CrmPermissionDO::getBizId, bizId) // 只能使用 SFunction 如果传 id 解析出来的 sql 不对
57   - .eq(CrmPermissionDO::getUserId, userId));
58   - }
59   - // 2.1 场景一:我负责的数据
  52 + // 场景一:我负责的数据
60 53 if (CrmSceneTypeEnum.isOwner(sceneType)) {
61 54 query.eq(ownerUserIdField, userId);
62 55 }
63   - // 2.2 场景二:我参与的数据(我有读或写权限,并且不是负责人)
  56 + // 场景二:我参与的数据(我有读或写权限,并且不是负责人)
64 57 if (CrmSceneTypeEnum.isInvolved(sceneType)) {
  58 + if (CrmPermissionUtils.isCrmAdmin()) { // 特殊逻辑:如果是超管,直接查询所有,不过滤数据权限
  59 + return;
  60 + }
65 61 query.innerJoin(CrmPermissionDO.class, on -> on.eq(CrmPermissionDO::getBizType, bizType)
66 62 .eq(CrmPermissionDO::getBizId, bizId)
67 63 .in(CrmPermissionDO::getLevel, CrmPermissionLevelEnum.READ.getLevel(), CrmPermissionLevelEnum.WRITE.getLevel()));
68 64 query.ne(ownerUserIdField, userId);
69 65 }
70   - // 2.3 场景三:下属负责的数据(下属是负责人)
  66 + // 场景三:下属负责的数据(下属是负责人)
71 67 if (CrmSceneTypeEnum.isSubordinate(sceneType)) {
72 68 AdminUserApi adminUserApi = SpringUtil.getBean(AdminUserApi.class);
73 69 List<AdminUserRespDTO> subordinateUsers = adminUserApi.getUserListBySubordinate(userId);
... ... @@ -79,22 +75,4 @@ public class CrmPermissionUtils {
79 75 }
80 76 }
81 77  
82   - /**
83   - * 构造 CRM 数据类型【批量】数据查询条件
84   - *
85   - * @param query 连表查询对象
86   - * @param bizType 数据类型 {@link CrmBizTypeEnum}
87   - * @param bizIds 数据编号
88   - * @param userId 用户编号
89   - */
90   - public static <T extends MPJLambdaWrapper<?>> void appendPermissionCondition(T query,
91   - Integer bizType, Collection<Long> bizIds, Long userId) {
92   - if (isCrmAdmin()) {// 管理员不需要数据权限
93   - return;
94   - }
95   - query.innerJoin(CrmPermissionDO.class, on ->
96   - on.eq(CrmPermissionDO::getBizType, bizType).in(CrmPermissionDO::getBizId, bizIds)
97   - .eq(CollUtil.isNotEmpty(bizIds), CrmPermissionDO::getUserId, userId));
98   - }
99   -
100 78 }
... ...