类型:数据问题 严重性:严重 状态:5.1.已关闭(复测通过)
| 问题描述 | 互联网用户可以查看到全网的数据,没有做权限控制,可以通过搜索项目名称、地市、调管单位等查询出来,或者在右下角输入页数之后回车查询到非本人新增的项目 |
|---|---|
| 一级模块 | 项目信息填报 |
| 二级模块 | / |
| 涉及类型 | 全部类型 |
| 是否阻断流程 | 是 |
| 处理人员 | 洪陈解 |
| 复测情况 | 复测通过 |
src/views/projectMaintain/manage/planPool.vueisCzry: localStorage.getItem('userShowPermission') === '4' —— 是否"场站人员"完全由浏览器 localStorage 判断,客户端可篡改_getProvinceAndMunicipalList() 中,非场站人员时把可选省份/地市列表替换为 data.allProList / data.allMulList(全网范围),供用户在搜索框自由勾选_getProjectImplList() 直接把 searchForm(省份、地市、关键词、页码等均为客户端可控字段)整体作为请求体传给后端 getProjectImplList 接口src/main/java/cn/csg/so/oms/in/newenergy/project/controller/NePjImplController.java 第44-49行:getProjectImplList(GetProjectImplListCondition condition) 直接将请求体透传给 Facade,未附加任何"当前登录用户所属单位/创建人"限定src/main/java/cn/csg/so/oms/in/newenergy/project/appservice/NePjImplAppService.java 第213-473行:buildQueryParams() 中 provinces / municipal / companyList 等全部是"客户端传了才过滤"的可选条件,唯一带用户身份校验的分支是第438-462行 condition.getProjectApply()——但这是一个客户端**可选**传参,不传就完全不生效src/main/resources/cn/csg/so/oms/in/energy/project/dbconfig/NePjImplQuerySQL_mysql.xml 第242、292行:<select id="NePjImplVO_selectByComplexConditions"> ... WHERE 1=1,后面全部是 <if> 可选片段,没有任何强制的用户范围子句NePjImplAppService.java 的 buildQueryParams()方法(第213行起)+ NePjImplController.java(第44行起)根本原因:接口把"当前登录人能看到哪些数据"完全交给前端传参决定,后端没有基于登录会话做强制的数据范围收口。互联网侧用户本应只能看到与自己相关(自己创建、或自己所属调管单位/地市)的项目,但只要客户端不传 provinces/municipal 或者传全部选项,服务端就会返回全网数据。
建议在 NePjImplAppService.getProjectImplList() 入口处,无论前端传了什么过滤条件,都强制叠加一层基于 UserUtils.getUser() 的服务端数据范围限定,而不是依赖可选的 projectApply 参数:
public IPage<NePjImplVO> getProjectImplList(GetProjectImplListCondition condition) {
// 新增:强制注入当前登录用户的数据范围,不依赖前端是否传参
UserUtils.User user = UserUtils.getUser();
applyMandatoryDataScope(condition, user); // 按创建人手机号 / 所属调管单位强制收窄
Map<String, Object> params = buildQueryParams(condition);
...
}
private void applyMandatoryDataScope(GetProjectImplListCondition condition, UserUtils.User user) {
// 互联网侧用户:仅能查看 creator_phone = 当前登录人手机号
// 或者其所属调管单位范围内的数据;管理员角色除外
if (!isAdminRole(user)) {
condition.setMandatoryCreatorPhone(user.getPhone());
condition.setMandatoryCompanyScope(getUserCompanyScope(user));
}
}
同时对应的 Mapper(NePjImplQuerySQL_mysql.xml / _dm.xml)需要在 WHERE 1=1 后面新增一个不可被前端绕过的强制片段:
<if test="mandatoryCreatorPhone != null and mandatoryCreatorPhone != ''">
AND (creator_phone = #{mandatoryCreatorPhone} OR company IN
<foreach collection="mandatoryCompanyScope" open="(" separator="," close=")" item="c">#{c}</foreach>)
</if>
前端 planPool.vue 中 isCzry 依赖 localStorage 的判断仅用于 UI 展示(是否显示更多筛选项),不能作为唯一的权限控制点;即使保留该逻辑,也必须以后端强制收口作为最终防线。