企业应用服务器与嵌入式 Tomcat 最大的区别,不是类名更多,而是配置和运行时分布在多个层级。一个 WAR 完全正常,服务器级 Filter、Deployment Plan、共享库、节点配置或启动 Agent 仍然可以改变请求链。
所以不能只问“包里有没有恶意类”,而要同时回答:
1 | 管理面配置了什么 |
本文只使用只读清点和诊断命令,不提供运行时植入代码。命令必须使用目标产品自带、版本匹配的管理工具执行。
一、先建立四个证据面
不同产品名称不同,但证据始终可以分成四层:
| 证据面 | 要回答的问题 | 常见来源 |
|---|---|---|
| 管理配置 | 控制面声明了什么 | Domain、Cell、Profile、Deployment Plan、server.xml |
| 节点运行时 | JVM 里实际装配了什么 | Deployment、Web Context、Handler、Servlet 注册表 |
| 类加载来源 | 对象的代码从哪里来 | Module、Shared Library、应用 ClassLoader、Agent |
| 集群一致性 | 哪些节点发生偏移 | Server Group、Managed Server、Node、Server |
四类服务器的主要差异:
| 产品 | Web 核心 | 配置层级 | 典型盲区 |
|---|---|---|---|
| WildFly | Undertow | Domain/Profile/Server Group/Deployment | 只查 WAR,漏掉 Undertow 子系统和 JBoss Module |
| WebLogic | WebApp Runtime | Domain/Managed Server/Plan/Shared Library | 只查 AdminServer,漏掉 Managed Server 当前状态 |
| WebSphere Traditional | Web Container | Cell/Node/Server/Application | 配置正确,但节点尚未同步 |
| Liberty | Feature 与 OSGi 组件 | server.xml/Feature/Application/Library | 把 Liberty 当成 Traditional WAS |
| Resin | WebApp | resin.xml/resin-web.xml/web.xml | 老旧节点缺少可信运行基线 |
应用内部可以先导出 Servlet 规范层,但这只是第一层视图:
1 | ServletContext context = request.getServletContext(); |
这段代码看不到服务器原生 Handler、共享库、管理模型和 Agent,也未必能取得实际实例。不要将它暴露为公网诊断接口;应在隔离副本、受控管理任务或启动基线采集器中执行。
二、WildFly 与 WebLogic 的只读核对
2.1 WildFly:管理模型必须对齐 Undertow 运行时
WildFly 请求链可以粗略表示为:
1 | Undertow 子系统配置 |
Standalone 模式下,先导出 Undertow 子系统:
1 | jboss-cli.sh --connect \ |
再读取部署资源:
1 | jboss-cli.sh --connect \ |
重点核对:
- custom-filter、expression-filter 及其 Host 引用;
- Deployment 与实际 Handler 链是否一致;
- 自定义类属于哪个 JBoss Module;
- JVM 参数是否加载 Agent 或额外模块;
- 管理审计日志中何时发生配置变更。
Domain 模式不能只连任意一台 Server。还要把 Profile、Server Group、Host Controller 和具体 Server 的结果对齐;同一个 Server Group 中只有一个节点出现额外 Handler,通常比全组一致的配置更可疑。
2.2 WebLogic:不要停在 AdminServer
WebLogic 的最终运行状态可能由以下内容共同决定:
1 | WAR / EAR |
WLST 进入 Domain Runtime 树后,可以只读枚举所有 Server 与应用运行时:
1 | connect( |
配置文件应只授予监控账号读取权限,并限制操作系统访问权限。不要把管理密码写入脚本、命令行参数或终端历史。随后逐节点比较:
- 应用部署目标、版本和当前状态;
- 原始制品与 Deployment Plan;
- Shared Library 和 Domain lib;
- Server Start 参数、ClassPath 与 Java Options;
- 每个 Managed Server 的 Servlet、Filter、Listener 和类来源。
AdminServer 显示“已部署”只代表控制面状态,不能证明每个 Managed Server 的当前对象一致。未取证前直接重新部署会改变运行时引用和时间线,不适合作为第一步。
三、WebSphere、Liberty 与 Resin 的关键差异
3.1 WebSphere Traditional
Traditional WebSphere 的层级是 Cell、Node、Server。先列出已安装应用和运行中的应用 MBean:
1 | print(AdminApp.list()) |
再保存以下状态:
- Cell 主配置库与应用部署元数据;
- Node Sync 状态和同步时间;
- 共享库、类加载顺序及模块覆盖规则;
- wsadmin 变更记录与安全审计日志;
- 每个 Server JVM 的启动参数和 Agent。
Cell 中配置正确,不等于 Node 已同步;应用制品相同,也不等于父优先或父最后的类加载结果相同。实际类的 ClassLoader 和 CodeSource 必须在对应 JVM 中确认。
3.2 Liberty
Liberty 由 Feature、OSGi Bundle 和配置片段装配运行时。首先核对:
server.xml及其 include 文件;- 启用的 Servlet 或 Jakarta EE Feature;
apps、dropins和外部应用路径;- commonLibraryRef、privateLibraryRef 与全局库;
server.env、bootstrap.properties、jvm.options。
可用产品自带命令生成诊断归档:
1 | server dump myserver --archive=myserver-dump.zip |
归档中的配置、已部署应用、OSGi Bundle、SCR 组件和运行环境可用于节点对比。Traditional 与 Liberty 内部对象不同,不能复用私有字段脚本;能复用的是“配置、运行时、类来源”三份清单。
3.3 Resin
Resin 应同时检查:
1 | resin.xml |
历史系统经常没有正常基线。此时应选择同版本、同补丁、同配置的干净节点,比较 WebApp、ContextPath、Servlet/Filter 映射、Filter 顺序、启动类路径和类来源,不能拿其他 Resin 版本的字段名直接套用。
四、按节点生成差异矩阵并恢复
每个节点至少生成一条记录:
1 | 节点、产品与补丁版本 |
按应用和集群分组后,优先调查以下差异:
- 只有一个节点出现额外 Handler;
- 名称相同,实际类或 CodeSource 不同;
- 管理面显示未部署,节点内却存在运行对象;
- 应用哈希一致,启动参数、Plan 或共享库不同;
- 边界代理有请求,应用访问日志没有记录。
处置前还要判断异常落在哪个变更面:
| 变更面 | 只重启 JVM | 只重部署应用 |
|---|---|---|
| 纯运行时 Servlet/Handler | 通常消失 | 可能消失 |
| Domain/Cell/Profile 配置 | 会重新装配 | 通常不受影响 |
| Deployment Plan | 会重新生效 | 会重新生效 |
| Shared Library/Module | 文件仍在就会加载 | 通常仍存在 |
| 启动 Agent | 重启时再次加载 | 不受影响 |
完整恢复顺序是:保存管理配置和审计历史,隔离异常节点,枚举实际对象及类来源,修复控制面与初始入口,从可信制品重建全部受影响节点,再用同一份清单复测。控制台显示正常、单个 WAR 哈希一致或某个节点重启成功,都不能单独构成恢复结论。