系统分析与修改记录.md 11 KB

修改日志

本文件记录了为修复登录和配置问题而对项目进行的所有修改。

2026-03-04

1. web/WEB-INF/web.xml

  • 修改内容: 注释掉了 *.do*.jsp/reportServletXSS 过滤器映射 (SecurityFilter)。
  • 修改原因: SecurityFilterSECURITY_HTTPREFERER 不匹配而报 403 错误,即使配置了白名单也依然存在问题。在开发/测试环境中禁用它可确保登录流程顺畅。

2. init_test_data.sql

  • 修改内容:
    • 更新了 AA10 表的 DELETE 语句,使用 AAA100ITEMCODE 代替 AAZ093
    • 增加了 FW_OPERATORFW_OPERATOR2RIGHTFW_RIGHTFW_OPERATEUNITFW_SYSORG 表的 DELETE 语句,以防止主键冲突。
    • 更新了 FW_OPERATOR 的插入语句:
      • admin: 密码重置为 admin (MD5: 21232f297a57a5a743894a0e4a801fc3)。
      • test: 密码重置为 test (明文, PWENCRYPT='0')。
    • 修复 ORA-00001: UK_FW_OPERATOR_LOGINID: 更新了 FW_OPERATORDELETE 逻辑,同时根据 LOGINIDOPERID 删除。
    • 修复 ORA-00001: PK_FW_OPERATOR2RIGHT: 增加了删除 ID 为 1, 2, 3 的 FW_OPERATOR2RIGHT 记录的语句。
    • 修复 ORA-00001: PK_AA01: 更新了 AA01DELETE 逻辑,根据 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

  • 修改内容: [已撤回] 之前重命名了 RememberMeRememberMe_ignored 以及 IMAGCHECKIMAGCHECK_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_LOG4EXCEPTIONDELETE 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.jspright_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 (兼容性函数)

  • 修改内容: 增加了 LOCATEf_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"))。
    • 通过重写 setContentTypesetCharacterEncoding 方法,拦截并忽略任何非 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 中的 <Connector ... URIEncoding="UTF-8" />
    • 注意: 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_VALIDATEFORMNEVER_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 包中,源码不可见)将这些字段定义为 intboolean,而前端提交的是 "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 表包含 PASSWORDPWENCRYPT 字段。
    • PWENCRYPT='1': 密码已加密(通常是 MD5)。
    • PWENCRYPT='0': 密码为明文。
    • 测试策略: 将所有测试账户的 PWENCRYPT 设为 '0' 且 PASSWORD 设为明文,同时前端 LOGIN_ENCRYPT 设为 '0',确保前后端均以明文传输和比对,排除加密算法不一致导致的问题。