{"record":{"id":"a72fc3ec3b714fa5","repo":"YunaiV/yudao-cloud","slug":"loginuser-d-table-s-s","errorCode":null,"errorMessage":"LoginUser(%d) Table(%s/%s) 未返回数据权限","messagePattern":"LoginUser\\((.+?)\\) Table\\((.+?)/(.+?)\\) 未返回数据权限","errorType":"exception","errorClass":"NullPointerException","httpStatus":null,"severity":"error","filePath":"yudao-framework/yudao-spring-boot-starter-biz-data-permission/src/main/java/cn/iocoder/yudao/framework/datapermission/core/rule/dept/DeptDataPermissionRule.java","lineNumber":107,"sourceCode":"    public Expression getExpression(String tableName, Alias tableAlias) {\n        // 只有有登陆用户的情况下，才进行数据权限的处理\n        LoginUser loginUser = SecurityFrameworkUtils.getLoginUser();\n        if (loginUser == null) {\n            return null;\n        }\n        // 只有管理员类型的用户，才进行数据权限的处理\n        if (ObjectUtil.notEqual(loginUser.getUserType(), UserTypeEnum.ADMIN.getValue())) {\n            return null;\n        }\n\n        // 获得数据权限\n        DeptDataPermissionRespDTO deptDataPermission = loginUser.getContext(CONTEXT_KEY, DeptDataPermissionRespDTO.class);\n        // 从上下文中拿不到，则调用逻辑进行获取\n        if (deptDataPermission == null) {\n            deptDataPermission = permissionApi.getDeptDataPermission(loginUser.getId()).getCheckedData();\n            if (deptDataPermission == null) {\n                log.error(\"[getExpression][LoginUser({}) 获取数据权限为 null]\", JsonUtils.toJsonString(loginUser));\n                throw new NullPointerException(String.format(\"LoginUser(%d) Table(%s/%s) 未返回数据权限\",\n                        loginUser.getId(), tableName, tableAlias.getName()));\n            }\n            // 添加到上下文中，避免重复计算\n            loginUser.setContext(CONTEXT_KEY, deptDataPermission);\n        }\n\n        // 情况一，如果是 ALL 可查看全部，则无需拼接条件\n        if (deptDataPermission.getAll()) {\n            return null;\n        }\n\n        // 情况二，即不能查看部门，又不能查看自己，则说明 100% 无权限\n        if (CollUtil.isEmpty(deptDataPermission.getDeptIds())\n            && Boolean.FALSE.equals(deptDataPermission.getSelf())) {\n            return new EqualsTo(null, null); // WHERE null = null，可以保证返回的数据为空\n        }\n\n        // 情况三，拼接 Dept 和 User 的条件，最后组合","sourceCodeStart":89,"sourceCodeEnd":125,"githubUrl":"https://github.com/YunaiV/yudao-cloud/blob/477be9dd49ab7223a972a6abdff0684d6423dec3/yudao-framework/yudao-spring-boot-starter-biz-data-permission/src/main/java/cn/iocoder/yudao/framework/datapermission/core/rule/dept/DeptDataPermissionRule.java#L89-L125","documentation":"The dept data-permission rule asks the permission service for a DeptDataPermissionRespDTO (cached on the LoginUser context). The RPC contract expects a non-null DTO; if getCheckedData() returns null, the rule deliberately throws NullPointerException with the userId and table in the message, because continuing would silently mean 'no restriction'.","triggerScenarios":"system_permission.getDeptDataPermission(userId) returns CommonResult with data=null (success:true but empty body) — e.g. the permission service has no rule rows for the user and returns null instead of a defaulted DTO, or a service refactor started returning null for admins.","commonSituations":"Partial deployment where the permission service is older/newer than the framework expectation; the user belongs to a tenant with no data-permission configuration and the service treats 'no config' as null; RPC deserialization producing null data on error-swallowing handlers.","solutions":["Make the remote service always return a non-null DeptDataPermissionRespDTO (default: all=false, dept ids empty) when no rules match.","Check the permission service logs for the exact userId at failure time.","Verify both services run compatible framework versions of the permission API.","If null is legitimately possible in your domain, override the rule to treat null as ALL (or none) explicitly instead of relying on the NPE."],"exampleFix":"// before (permission service)\nif (CollUtil.isEmpty(rules)) return success(null); // -> NPE in rule\n\n// after\nif (CollUtil.isEmpty(rules)) {\n    DeptDataPermissionRespDTO dto = new DeptDataPermissionRespDTO();\n    dto.setAll(false); dto.setDeptIds(Collections.emptySet()); dto.setSelfIds(Collections.emptySet());\n    return success(dto);\n}","handlingStrategy":"try-catch","validationCode":"DeptDataPermissionRespDTO dto = loginUser.getContext(CONTEXT_KEY, DeptDataPermissionRespDTO.class);\nif (dto == null) {\n    CommonResult<DeptDataPermissionRespDTO> r = permissionApi.getDeptDataPermission(userId);\n    dto = r.isSuccess() ? r.getData() : null;\n    if (dto == null) { /* treat as no-permission or all — your policy */ }","typeGuard":null,"tryCatchPattern":"try {\n    expression = rule.getExpression(tableName, tableAlias, user);\n} catch (NullPointerException e) {\n    if (e.getMessage() != null && e.getMessage().contains(\"未返回数据权限\")) {\n        log.error(\"permission service returned null for user {}; failing closed\", user.getId());\n    }\n    throw e;\n}","preventionTips":["Contract-test the permission service: getDeptDataPermission must never return success(null)","Return a defaulted DTO (all=false, empty sets) instead of null when no rules exist"],"tags":["yudao","data-permission","rpc","null-safety","tenant"],"backgroundTag":null,"analyzedSha":"477be9dd49ab7223a972a6abdff0684d6423dec3","analyzedAt":"2026-08-14T13:35:31.121Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}