Struts2 从入门到放弃?不,这些核心知识你依然需要掌握

21 阅读3分钟

一、写在前面

Struts2 曾是 Java Web 开发的事实标准之一,尤其是在 2010 年左右,几乎是企业级 SSH(Struts2 + Spring + Hibernate)框架中的“门面担当”。随着 Spring MVC 和 Spring Boot 的崛起,Struts2 逐渐退出主流技术栈,但你仍然可能在许多传统企业的系统中与它“狭路相逢”。

了解 Struts2,未必是为了追赶潮流,而是为了在生产环境出现问题时,能够从容应对。

二、Struts2 核心架构

1. 请求处理流程

text

HttpServletRequest 
    → FilterDispatcher(StrutsPrepareAndExecuteFilter)
    → ActionProxy 
    → ActionInvocation 
    → Interceptor(拦截器链)
    → Action 
    → Result
    → HttpServletResponse
  • 核心控制器StrutsPrepareAndExecuteFilter(早期版本为 FilterDispatcher
  • Action:业务逻辑处理单元,返回逻辑视图名
  • Result:视图渲染(JSP、FreeMarker、JSON 等)

2. 一个简单的 Action 示例

java

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

<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

<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

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

<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-045Content-Type 头解析升级至 2.3.32 / 2.5.10.1
S2-057namespace 配置不当升级至 2.5.16 / 2.3.35
S2-061OGNL 沙盒绕过升级至 2.5.26+

✅ 安全建议

  • 始终使用最新稳定版的 Struts2(目前 2.5.x 及以上)
  • 避免直接使用 altSyntax、通配符 action 配置不当
  • 对用户输入进行严格校验
  • 使用 struts2-security 插件或增加安全拦截器

六、和 Spring MVC 的对比

特性Struts2Spring MVC
核心设计基于类拦截,每个请求都会创建一个 Action 实例基于方法拦截,Controller 是单例
参数绑定基于 OGNL + 属性驱动基于方法参数 + @RequestParam
视图技术JSP + 标签库更灵活,支持 Thymeleaf、JSON 等
性能稍差(值栈、OGNL 解析开销)更优
安全性历史上漏洞较多相对较少

七、维护老项目的小技巧

如果你们系统仍然使用 Struts2,建议:

  1. 升级到允许范围内的最新版本(如 2.5.33)
  2. 将 JSP 中的 <s:property> 替换为 EL 表达式(降低 OGNL 触发风险)
  3. 关闭动态方法调用 <constant name="struts.enable.DynamicMethodInvocation" value="false"/>
  4. 避免使用通配符映射 action="*_*" class="{1}Action" method="{2}"
  5. 考虑逐步迁移到 Spring Boot + Spring MVC

八、结语

Struts2 就像一位“老将军”,虽然已不复当年之勇,但在很多战场上依然坚守岗位。如果你是一名企业级开发人员,不求精通,但至少应当理解它的请求流转、值栈原理以及常见漏洞。

学 Struts2,不是为了跟随潮流,而是为了“优雅地维护”——你总会在某个客户现场,和这位老熟人重逢。