一、写在前面
Struts2 曾是 Java Web 开发的事实标准之一,尤其是在 2010 年左右,几乎是企业级 SSH(Struts2 + Spring + Hibernate)框架中的"门面担当"。随着 Spring MVC 和 Spring Boot 的崛起,Struts2 逐渐退出主流技术栈,但你仍然可能在许多传统企业的系统中与它"狭路相逢"。
了解 Struts2,未必是为了追赶潮流,而是为了在生产环境出现问题时,能够从容应对。
二、Struts2 核心架构
1. 请求处理流程
text
markdown
HttpServletRequest
→ FilterDispatcher(StrutsPrepareAndExecuteFilter)
→ ActionProxy
→ ActionInvocation
→ Interceptor(拦截器链)
→ Action
→ Result
→ HttpServletResponse
- 核心控制器 :
StrutsPrepareAndExecuteFilter(早期版本为FilterDispatcher) - Action:业务逻辑处理单元,返回逻辑视图名
- Result:视图渲染(JSP、FreeMarker、JSON 等)
2. 一个简单的 Action 示例
java
scala
public class LoginAction extends ActionSupport {
private String username;
private String password;
public String execute() {
if ("admin".equals(username) && "123456".equals(password)) {
return SUCCESS;
}
return ERROR;
}
// getters / setters...
}
对应的 struts.xml:
xml
ini
<action name="login" class="com.demo.LoginAction">
<result name="success">/welcome.jsp</result>
<result name="error">/login.jsp</result>
</action>
三、OGNL 与 ValueStack
这是 Struts2 最"迷人"也最"伤人"的部分。
1. ValueStack(值栈)
- 存放 Action 对象、模型对象、临时变量
- 使用 OGNL 表达式访问值栈中的数据
2. OGNL 表达式示例
jsp
xml
<s:property value="username" /> <!-- 调用 getUsername() -->
<s:property value="#session.userId" /> <!-- 访问 Session 域 -->
<s:set name="myVar" value="'hello'" /> <!-- 创建临时变量 -->
也正是 OGNL 强大的表达式能力 + 参数自动填充机制,让 Struts2 历史上爆发过大量远程代码执行漏洞(S2-xxx)。
四、拦截器(Interceptor)
拦截器是 Struts2 的 AOP 实现,用于实现权限校验、日志、输入校验、文件上传等横切关注点。
自定义拦截器:
java
scala
public class AuthInterceptor extends AbstractInterceptor {
public String intercept(ActionInvocation invocation) throws Exception {
Map<String, Object> session = invocation.getInvocationContext().getSession();
if (session.get("user") == null) {
return "login";
}
return invocation.invoke();
}
}
在 struts.xml 中配置拦截器栈:
xml
ini
<interceptors>
<interceptor name="auth" class="com.demo.AuthInterceptor"/>
<interceptor-stack name="myStack">
<interceptor-ref name="defaultStack"/>
<interceptor-ref name="auth"/>
</interceptor-stack>
</interceptors>
五、常见漏洞与安全提醒
Struts2 相对出名(或者说"出名")的一点就是频繁的 RCE 漏洞。
| 漏洞编号 | 触发点 | 修复方式 |
|---|---|---|
| S2-045 | Content-Type 头解析 | 升级至 2.3.32 / 2.5.10.1 |
| S2-057 | namespace 配置不当 | 升级至 2.5.16 / 2.3.35 |
| S2-061 | OGNL 沙盒绕过 | 升级至 2.5.26+ |
✅ 安全建议:
- 始终使用最新稳定版的 Struts2(目前 2.5.x 及以上)
- 避免直接使用
altSyntax、通配符 action 配置不当 - 对用户输入进行严格校验
- 使用
struts2-security插件或增加安全拦截器
六、和 Spring MVC 的对比
| 特性 | Struts2 | Spring MVC |
|---|---|---|
| 核心设计 | 基于类拦截,每个请求都会创建一个 Action 实例 | 基于方法拦截,Controller 是单例 |
| 参数绑定 | 基于 OGNL + 属性驱动 | 基于方法参数 + @RequestParam |
| 视图技术 | JSP + 标签库 | 更灵活,支持 Thymeleaf、JSON 等 |
| 性能 | 稍差(值栈、OGNL 解析开销) | 更优 |
| 安全性 | 历史上漏洞较多 | 相对较少 |
七、维护老项目的小技巧
如果你们系统仍然使用 Struts2,建议:
- 升级到允许范围内的最新版本(如 2.5.33)
- 将 JSP 中的
<s:property>替换为 EL 表达式(降低 OGNL 触发风险) - 关闭动态方法调用
<constant name="struts.enable.DynamicMethodInvocation" value="false"/> - 避免使用通配符映射
action="*_*" class="{1}Action" method="{2}" - 考虑逐步迁移到 Spring Boot + Spring MVC
八、结语
Struts2 就像一位"老将军",虽然已不复当年之勇,但在很多战场上依然坚守岗位。如果你是一名企业级开发人员,不求精通,但至少应当理解它的请求流转、值栈原理以及常见漏洞。
学 Struts2,不是为了跟随潮流,而是为了"优雅地维护"------你总会在某个客户现场,和这位老熟人重逢。