SMT-ERP 项目初始提交

This commit is contained in:
2026-10-13 16:15:00 +08:00
commit d8643761f5
876 changed files with 198756 additions and 0 deletions

View File

@@ -0,0 +1,89 @@
# 2026-10-12 代码审查修复及上线说明
本次以不改变正常业务计算、单据状态流转和原有数据为原则。认证变化已获调用方同步调整的确认。没有连接生产数据库、轮换真实凭据或发布服务。
## 审查项处理结果
| 审查项 | 本次处理 | 兼容性与剩余事项 |
| --- | --- | --- |
| C1 明文凭据 | application.yml 的数据库用户名、数据库密码、Redis 密码改为外部注入;生成器配置移除默认用户名及密码 | 部署前配置环境变量;已泄露的真实口令仍须由部署人员轮换,Git 历史中的旧值没有清除 |
| C2 LIKE 注入 | 4 个 Mapper、14 处表达式改为 `concat('%', #{参数}, '%')` | 保留模糊搜索、中文、单引号和 `%` / `_` 通配符语义,不对合法搜索词做截断或删字符 |
| C3 匿名同步 | 两个 sync 接口仅接受独立 API Key 或已有 ERP 登录会话 | 外部调用方必须增加请求头,未配置密钥不允许匿名通过 |
| C3 旧盘点 | Login 返回独立短期 token;盘点业务接口要求认证;校验 USER_ID 和当前身份、盘点单所属盘点人;沿用登录失败限流 | 保留原大写字段和响应包装,增加 TOKEN 字段;禁止伪造他人身份与跨盘点人操作 |
| C3 白名单 | 移除匿名 tenantClean;白名单前缀加路径边界;同步路径精确匹配 | 静态 Excel 模板仅精确 GET 公开,保留原 `<a>` / `window.open` 下载;模板不含业务数据 |
| C4 eval | WebSocket 使用 JSON.parse;菜单占位符使用显式映射 | 非 JSON 消息会被忽略;未知菜单表达式原样保留且不执行,见下文支持列表 |
| H1 MD5/弱默认密码 | 完成入口核对及迁移设计,尚未改动存储与登录协议 | 现有库 password 为 varchar(50),不能直接存 BCrypt;详见迁移步骤。此风险尚未关闭 |
| H2 排序/Example | 保留已存在的 4 处字段及方向白名单,新增真实 MyBatis BoundSql 回归;分页排序加强为字段/方向语法校验 | Example 条件由内部方法构造,未发现 Controller 接收外部 Example;不机械替换生成器 `${criterion.condition}` |
| H3 公告 XSS | 使用 DOMPurify 3.4.16 在展示时净化 HTML | 保留普通 HTML 排版;历史记录不改写;后端存储端的富文本策略需覆盖其他消费者后再实施 |
| H4 老依赖 | JDBC 关闭 allowMultiQueries;新增 DOMPurify;修复 Java 11 测试工具兼容 | Mapper 静态检查未发现分号批量语句;Spring Boot、axios、jQuery、fastjson 大版本升级仍需专项回归,未宣称已消除所有依赖漏洞 |
| H5 会话/插件 | Redis 会话有效期支持配置,默认沿用原 259200 秒 | 报告中的 servlet 36000 秒不是实际 ERP token 有效期;插件安装原本就校验管理员,不是普通登录用户可安装 |
| M1 异常 | HTTP 关闭失败记录日志、保留 POST 异常原因;日期格式试探仅捕获 ParseException;盘点内部错误记录日志而不向客户端暴露堆栈 | 日期格式依次尝试是合理流程,全部失败仍按原逻辑抛出业务错误;不改日期解析宽松程度 |
| M2 API 权限 | 补齐本次同步与盘点入口及盘点身份/数据归属校验 | 全系统按钮权限到 API 权限的映射尚未实施,需要单独梳理角色、租户、共享数据和插件接口 |
| M3 测试 | 新增认证、盘点权限、SQL、前端 XSS/eval 回归;移除 Maven 跳过测试编译配置;升级测试插件和测试 mock 工具以支持 Java 11 | 不使用真实数据库和 Redis,不等同于完整业务验收 |
| L1/L2 | printStackTrace 改结构化日志;保持局部 SimpleDateFormat | 局部实例无共享线程安全问题;替换日期 API 可能改变宽松解析、时区等行为,本次不做无必要重构 |
## 部署前配置
使用服务管理器、容器配置或配置中心注入:
| 变量 | 用途 | 默认 |
| --- | --- | --- |
| DB_USERNAME | 数据库账号 | 无,必填 |
| DB_PASSWORD | 数据库密码 | 无,必填 |
| REDIS_PASSWORD | Redis 密码 | 无,必填;无密码的 Redis 显式配置为空 |
| SYNC_API_KEY | 外部入库同步专用密钥 | 空;空值时仅 ERP 会话可以调用 sync |
| INVENTORY_TOKEN_TTL_SECONDS | 旧盘点 token 绝对有效期 | 3600 秒;最低 60 秒 |
| ERP_SESSION_TIMEOUT_SECONDS | ERP Redis 会话滑动有效期 | 259200 秒,与原值一致;最低 60 秒 |
也可继续通过外部 Spring 配置覆盖 `spring.datasource.*` / `spring.redis.*`。不要把真实凭据重新写入仓库。MyBatis Generator 的 `${DB_USERNAME}` / `${DB_PASSWORD}` 是 Java 系统属性替换,运行生成器时需由本地构建配置提供这两个系统属性,不能假定它会自动读取同名环境变量。
生产数据库/Redis 凭据轮换应与服务配置更新一起安排,确认连接成功后撤销旧凭据;本次没有自行操作线上账号。同步请求和登录请求须经 HTTPS 或受保护的内部连接,不能在访问日志中记录认证头。
## 同步调用方改造
两个地址保持不变:
- `POST /apis/smtInbound/sync`
- `POST /apis/headquartersInbound/sync`
请求体和成功返回结构不变,增加 `X-Sync-Api-Key: <SYNC_API_KEY>`。密钥仅允许上述两个 POST 接口,不赋予其他接口权限。已有 `X-Access-Token` ERP 会话仍可调用。未提供认证、密钥错误或密钥未配置时返回 HTTP 401。
静态下载 `/apis/smtInbound/template`、`/apis/headquartersInbound/template` 保持匿名 GET;其他方法以及名称相似的路径不放行。`tenantClean` 现在必须登录,相关插件调用方需要携带原 ERP 会话头。
## 旧盘点客户端改造
1. 原样 POST `/apis/n2tpm/webmvc/Inventory/Login` 或 `/apis/webmvc/Inventory/Login`,继续传 USER_NAME、PASSWORD。没有 context path 的部署同样支持这两个路径。
2. 成功响应仍是 `{code, msg, data}`;`data.RESULT`、`data.USER_ID`、`data.DATA_AUTH` 保留,新增 `data.TOKEN`。
3. 后续所有查询、保存、接单、上传请求增加 `X-Inventory-Token: <data.TOKEN>`。HTTP 401 时重新登录,不能只凭 USER_ID 重试。
4. USER_ID 必须是当前登录用户;盘点单必须分配给该用户;批量 DATA 中每条操作都校验。管理员如需代盘,应先通过原 ERP 分配流程调整盘点人,不能伪造 USER_ID。
5. token 使用独立 Redis 命名空间并绝对过期,读取不会续期,不能当作 ERP 的 X-Access-Token 使用。也支持使用已有 ERP 会话调用盘点接口;同时提供两个头时优先验证盘点 token,避免身份混淆。
6. 用户被停用/删除后,即使 token 未到期,业务服务也会拒绝操作。错误密码沿用现有 ERP 的账号限流策略。
## 菜单 URL 兼容范围
支持 `{{ window._CONFIG.domianURL }}`、其单/双引号方括号形式,以及 `window.location.origin`、`window.location.host`、`window.location.protocol`。普通菜单 URL 不变。其他自定义表达式不执行,需在核实数据库菜单配置后将确实需要的非敏感值增加到 `menuUrl.js` 的显式映射。
公告展示依赖 [DOMPurify 官方发行版](https://github.com/cure53/DOMPurify/releases/tag/3.4.16)。当前使用 HTML profile,SVG/MathML/脚本等内容不保留。现有项目忽略 package-lock.json,本次遵循仓库规则,新增依赖在 package.json 中固定版本。
## H1:口令迁移实施顺序(尚未执行)
1. 先在预发布验证并扩展 password 至至少 varchar(100),增加算法标识或采用带算法前缀的存储格式;增加 must_change_password 标记。备份用户表,禁止批量覆盖旧口令。
2. 当前网页在登录、注册、改密时发送 MD5,旧盘点发送明文后由服务端转 MD5,微信绑定也用 SQL 等值比较密码。必须把验证集中到同一密码服务,覆盖所有入口和密码写入路径,不能只改 insertUser。
3. 过渡期可在服务端对旧协议的 MD5 凭据做 BCrypt,并在成功验证旧记录时有条件升级存储;明确这只改善数据库泄露风险,网络中的 MD5 仍是可重放凭据,必须配合 HTTPS。不得把 BCrypt 值写回仍按 MD5 等值比较的旧程序。
4. 新协议传原始密码并由服务端 BCrypt/Argon2 校验;使用明确协议版本区分,不根据密码是否“看起来像 MD5”猜测。提供兼容期,统计旧客户端后再停用旧协议。
5. 新建/重置用户使用单次临时随机密码或管理员指定强密码,返回一次并强制首次改密。先补管理界面的交付流程,避免用户创建成功却无法得知初始密码。旧弱口令账号的会话限制和强制改密需要前后端一起实现。
6. 回归:旧账号、新账号、改密、重置、停用、限流、默认密码提醒、注册、微信绑定/登录、旧盘点,以及并发登录时的条件升级。升级后回滚代码必须继续能验证新哈希,不能直接回滚到只支持 MD5 的旧包。
## H4/M2 后续专项范围(尚未执行)
- 框架升级先验证插件框架 2.2.1、MyBatis 扩展、Redis、Swagger 与目标 Spring/JDK 的兼容矩阵,再选择受支持目标版本,不在这次安全补丁中跨代替换依赖。
- axios/jQuery 升级需要覆盖拦截器、错误对象、上传下载、请求取消,以及打印、图片预览等依赖插件;fastjson 迁移需要验证实体绑定、日期/数值和插件序列化。
- API 权限按现有菜单/按钮建立“HTTP 方法 + 路由 + 操作权限”映射,再覆盖管理员、普通用户、租户和插件接口的正反用例;不要直接把前端菜单可见性当成完整数据授权。
## 验证方式及边界
本地验证:后端 23 个测试通过;前端 3 组安全回归通过;生产构建成功退出。前端构建仍提示资源体积超限及 sass-loader 缺失,这些警告未在本次安全修复中处理。修改文件通过 UTF-8、BOM/替换字符和 `git diff --check` 检查。
后端运行 `mvn -B test`;前端运行 `npm run test:security` 和 `npm run build`。后端测试使用真实 MyBatis XML 生成 SQL,验证 14 个 LIKE 参数不进入 SQL 文本、正常排序继续生效、注入排序不生效;认证与盘点用例使用 mock,不连接外部服务。
上线前仍需在预发布环境完成:正常 ERP 登录、入库同步、新/旧盘点登录、本人盘点单接收和保存、图片上传、查询排序、模板下载、公告展示、插件管理员操作。自测通过不能保证所有业务完全无影响,尤其是外部客户端与数据库中的自定义菜单配置。