# 修改日志 本文件记录了为修复登录和配置问题而对项目进行的所有修改。 ## 2026-03-04 ### 1. `web/WEB-INF/web.xml` - **修改内容**: 注释掉了 `*.do`、`*.jsp` 和 `/reportServlet` 的 `XSS` 过滤器映射 (`SecurityFilter`)。 - **修改原因**: `SecurityFilter` 因 `SECURITY_HTTPREFERER` 不匹配而报 403 错误,即使配置了白名单也依然存在问题。在开发/测试环境中禁用它可确保登录流程顺畅。 ### 2. `init_test_data.sql` - **修改内容**: - 更新了 `AA10` 表的 `DELETE` 语句,使用 `AAA100` 和 `ITEMCODE` 代替 `AAZ093`。 - 增加了 `FW_OPERATOR`、`FW_OPERATOR2RIGHT`、`FW_RIGHT`、`FW_OPERATEUNIT`、`FW_SYSORG` 表的 `DELETE` 语句,以防止主键冲突。 - 更新了 `FW_OPERATOR` 的插入语句: - `admin`: 密码重置为 `admin` (MD5: `21232f297a57a5a743894a0e4a801fc3`)。 - `test`: 密码重置为 `test` (明文, `PWENCRYPT='0'`)。 - **修复 ORA-00001: UK_FW_OPERATOR_LOGINID**: 更新了 `FW_OPERATOR` 的 `DELETE` 逻辑,同时根据 `LOGINID` 和 `OPERID` 删除。 - **修复 ORA-00001: PK_FW_OPERATOR2RIGHT**: 增加了删除 `ID` 为 1, 2, 3 的 `FW_OPERATOR2RIGHT` 记录的语句。 - **修复 ORA-00001: PK_AA01**: 更新了 `AA01` 的 `DELETE` 逻辑,根据 `AAA001` (包含 `CHECKIMAGE_SB`) 以及 `AAZ499` ID 范围 (3000-3008) 进行删除。 - 在 `AA01` 表中增加了多条 `SECURITY_HTTPREFERER` 配置,以支持多种 Referer 格式。 - 增加了 `CHECKIMAGE_SB = '0'` 配置以禁用登录验证码。 - **修改原因**: - 修复插入字典数据时 `UK_AAA100_ITEMCODE` 的唯一约束冲突。 - 确保测试账户可用且密码已知。 - 通过配置 Referer 白名单防止 403 错误(虽然过滤器已禁用,但这可作为后续参考)。 ### 3. `web/jsp/framework/security/loginsb_i.jsp` - **修改内容**: **[已撤回]** 之前重命名了 `RememberMe` 为 `RememberMe_ignored` 以及 `IMAGCHECK` 为 `IMAGCHECK_ignored`。根据用户要求,此修改已撤回。 - **注意**: 如果后端 Action 严格要求 `RememberMe` (boolean/int) 和 `IMAGCHECK` (string/int) 的特定类型,Struts2 的 `ConversionErrorInterceptor` 异常可能会再次出现。 ### 4. `init_test_data.sql` (最新更新) - **修改内容**: - 将所有测试账户 (`admin`, `test`, `admin_123456`) 设置为使用**明文密码**。 - 将所有用户的 `PWENCRYPT` 设置为 `'0'`。 - 增加了 `UNSAFE_VALIDATEFORM` 配置为 `NEVER_MATCH_THIS_TOKEN`,以防止配置为空时导致客户端验证错误。 - 增加了 `LOGIN_ENCRYPT` 配置为 `0`,以匹配明文密码设置。 - 增加了 `LOGIN_TIP` 配置为空字符串。 - 增加了 `DELETE FROM ZJRS_YWXT.FW_LOG4EXCEPTION` 和 `DELETE FROM ZJRS_YWXT.FW_LOG4LOGIN` 语句。 - **修改原因**: - 避免 MD5 哈希问题,简化登录测试。 - 修复客户端“不安全字符”验证错误,该错误在 `UNSAFE_VALIDATEFORM` 为空时发生(JS `split` 结果为 `[""]`,会匹配所有内容)。 - 修复 `ORA-00001: unique constraint (ZJRS_YWXT.PK_FW_LOG4EXCEPTION_ID) violated` 错误。系统尝试记录异常时,序列 `SEQ_FW_LOG4EXCEPTION` 生成的 ID 与表中现有数据冲突。清理该表可临时解决此问题,并允许查看实际触发异常的根本原因。 ### 5. `web/jsp/sysmngr/web/authmngr/right_left.jsp` 和 `right_auth.jsp` - **修改内容**: 将代码中的 `LOCATE` 函数替换为 Oracle 的 `INSTR` 函数。 - **修改原因**: `LOCATE` 是 MySQL 函数,Oracle 不支持,导致 `ORA-00904` 错误。 - **详细变更**: - `LOCATE(B.RIGHTID,A.RIGHTID) = 1` -> `INSTR(A.RIGHTID, B.RIGHTID) = 1` - `LOCATE(BAE001,'"+orgCode+"') = 1` -> `INSTR('"+orgCode+"', BAE001) = 1` - 注意:`LOCATE(substr, str)` 参数顺序与 `INSTR(str, substr)` 相反。 ### 6. `init_test_data.sql` (兼容性函数) - **修改内容**: 增加了 `LOCATE` 和 `f_length` 自定义函数的创建语句。 - **修改原因**: - 系统中部分代码(如 `f_length(TREEKEY)`)使用了 Oracle 不支持的函数,且源码难以定位或修改。 - 为了彻底解决 `ORA-00904` 错误,在数据库层面实现了这两个函数的兼容版本: - `LOCATE`: 封装 `INSTR`。 - `f_length`: 封装 `LENGTH`。 ### 7. `web/WEB-INF/web.xml` (新增过滤器) - **修改内容**: 增加了 `AjaxEncodingFilter` 过滤器配置。 - **新建文件**: `src/cn/sinobest/sysmngr/comm/filter/AjaxEncodingFilter.java` - **修改原因**: - 修复 `ajaxAdapter.do` 接口返回的 JSON 数据中文乱码问题。 - 该接口由 JAR 包提供(源码不可修改),默认响应编码可能为 ISO-8859-1。 - 过滤器强制将响应编码设置为 `UTF-8`。 ### 8. `src/cn/sinobest/sysmngr/comm/filter/AjaxEncodingFilter.java` (增强版) - **修改内容**: 使用 `HttpServletResponseWrapper` 包装响应对象。 - **修改原因**: - 仅设置 `response.setCharacterEncoding("UTF-8")` 可能无效,因为后续的 Action 代码(在 JAR 包中)可能会覆盖此设置(例如调用 `setContentType("text/html;charset=GBK")`)。 - 通过重写 `setContentType` 和 `setCharacterEncoding` 方法,拦截并忽略任何非 UTF-8 的编码设置尝试,强制确保最终响应为 UTF-8。 - **二次增强**: - 增加了 `request.setCharacterEncoding("UTF-8")`,确保请求参数(POST Body)也能被正确解析。 - 增强了 `setContentType` 的逻辑:不仅忽略非 UTF-8,还会尝试将 `charset=GBK` 替换为 `charset=UTF-8`。 - 重写了 `getWriter()`,在获取 Writer 之前再次强制设置 Response 编码为 UTF-8。 ### 9. `src/cn/sinobest/sysmngr/comm/filter/AjaxEncodingFilter.java` (GBK 版) - **修改内容**: 将强制编码从 `UTF-8` 改为 `GBK`。 - **修改原因**: - 经过测试,`ajaxAdapter.do` 返回的数据被浏览器解析为 `UTF-8` 时出现乱码(例如 `湛` 显示为 `տ`),这表明后端实际上发送的是 `GBK` 编码的字节流。 - 强制浏览器使用 `GBK` 解码应该能正确显示中文。 ## 技术分析与问题诊断 ### 3. 中文乱码问题分析 (Tomcat vs 代码) - **Tomcat 配置问题 (`URIEncoding`)**: - **现象**: GET 请求参数(如 `whereCls=...`)包含中文时乱码。 - **原因**: Tomcat 默认 `URIEncoding` 为 ISO-8859-1(旧版)或 UTF-8(新版)。如果应用预期 UTF-8 但 Tomcat 使用 ISO-8859-1,则参数乱码。 - **解决**: 修改 `server.xml` 中的 ``。 - **注意**: `ajaxAdapter.do` 是 POST 请求,参数在 Body 中,通常由 `CharacterEncodingFilter` (`web.xml`) 处理,不受 `URIEncoding` 影响。 - **代码/响应编码问题 (`Content-Type`)**: - **现象**: 接口返回的 JSON 数据(响应体)乱码(如 `{"title":"???"}`)。 - **原因**: 后端代码未显式设置 `response.setCharacterEncoding("UTF-8")`,导致服务器使用默认编码(通常是 ISO-8859-1)发送数据,而浏览器/客户端按 UTF-8 解析。 - **解决**: - 修改源码(如 `TreeAction.java`)显式设置编码。 - 对于无法修改源码的 JAR 包接口(如 `ajaxAdapter.do`),添加过滤器 (`AjaxEncodingFilter`) 强制设置响应编码。 ### 4. 登录流程与逻辑问题分析 #### 前端逻辑 (`loginsb_i.jsp`) - **不安全字符验证**: - 代码: `var fwUnsafeValidateFormTokens = "<%=UNSAFE_VALIDATEFORM%>".split("|");` - **问题**: 当数据库配置 `UNSAFE_VALIDATEFORM` 为空时,`split("|")` 返回 `[""]`(包含一个空字符串的数组)。 - **后果**: `indexOf("")` 对任何字符串都返回 0,导致所有输入都被误判为包含“不安全字符”,阻止登录。 - **修复**: 在 SQL 中设置 `UNSAFE_VALIDATEFORM` 为 `NEVER_MATCH_THIS_TOKEN`,避免空字符串匹配。 - **密码加密逻辑**: - 代码: ```javascript if (isEncrypty == '1') { var _1 = frm._1_.value; t = stringToHex(encrypt(_1,pw)); } ``` - **参数**: `isEncrypty` 来自数据库配置 `LOGIN_ENCRYPT`。 - **问题**: 如果配置为 '1',前端会使用 `encrypt.js` 进行加密。但如果后端没有对应的解密逻辑(或期望 MD5),会导致密码不匹配。 - **现状**: 测试环境数据库中 `admin` 等账户密码已被重置为明文或简单的 MD5。为了简化调试,我们将 `LOGIN_ENCRYPT` 设为 '0'(不加密),并让所有账户使用明文密码。 #### 后端逻辑与异常 - **Struts2 类型转换异常 (`ConversionErrorInterceptor`)**: - **现象**: 提交登录表单时抛出异常。 - **原因**: 前端表单包含 `RememberMe` (checkbox) 和 `IMAGCHECK` (text) 字段。如果 Action 类(位于 JAR 包中,源码不可见)将这些字段定义为 `int` 或 `boolean`,而前端提交的是 "on" (checkbox 默认值) 或 "" (空字符串),Struts2 默认的拦截器会尝试转换类型并失败。 - **尝试修复**: 曾尝试重命名 JSP 中的字段以绕过 Action 绑定,但用户要求撤回。 - **当前状态**: 依赖后端 Action 的兼容性。如果再次出现,可能需要恢复 JSP 修改或寻找其他绕过方法。 - **数据库 ID 冲突 (`ORA-00001`)**: - **现象**: `unique constraint (ZJRS_YWXT.PK_FW_LOG4EXCEPTION_ID) violated`。 - **原因**: 系统在捕获并记录异常(可能是登录失败或其他逻辑错误)到 `FW_LOG4EXCEPTION` 表时,使用的序列 `SEQ_FW_LOG4EXCEPTION` 生成的 ID 小于表中已存在的最大 ID。 - **修复**: 通过 `DELETE FROM ZJRS_YWXT.FW_LOG4EXCEPTION` 清空表数据,消除 ID 冲突,从而允许系统正常写入新的异常日志,以便我们看到真正的错误原因。 ### 2. 加密解密过程分析 - **前端加密 (`encrypt.js`)**: - 使用自定义的加密算法(非标准 AES/DES),依赖于从后端获取的密钥 `_1_` (session 中的 `des_key`)。 - 函数 `encrypt(key, password)` 将密码转换为加密字符串,再通过 `stringToHex` 转为十六进制。 - 只有当 `LOGIN_ENCRYPT = '1'` 时启用。 - **后端验证**: - 数据库 `FW_OPERATOR` 表包含 `PASSWORD` 和 `PWENCRYPT` 字段。 - `PWENCRYPT='1'`: 密码已加密(通常是 MD5)。 - `PWENCRYPT='0'`: 密码为明文。 - **测试策略**: 将所有测试账户的 `PWENCRYPT` 设为 '0' 且 `PASSWORD` 设为明文,同时前端 `LOGIN_ENCRYPT` 设为 '0',确保前后端均以明文传输和比对,排除加密算法不一致导致的问题。