You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于Java与Struts2框架的老旧Web应用CSRF攻击防护方案咨询

基于Java+Struts2老旧Web应用的CSRF防护方案

优先使用Struts2原生CSRF拦截器

如果你的Struts2版本在2.1.6及以上,直接用官方自带的CSRF拦截器是最省心的方案,不用自己造轮子:

  • 在struts.xml中修改拦截器栈,把csrf拦截器加入默认栈:
<interceptors>
    <interceptor-stack name="defaultStack">
        <interceptor-ref name="csrf"/>
        <!-- 保留原有其他拦截器配置 -->
    </interceptor-stack>
</interceptors>
<!-- 全局应用该栈 -->
<default-interceptor-ref name="defaultStack"/>
  • 前端表单页面中,用Struts2标签生成CSRF token隐藏域:<s:token/>,这个标签会自动在session中生成并存储token,提交表单时拦截器会自动校验请求中的token与session中的是否一致。
  • 若涉及AJAX请求,需要手动把token放到请求头(比如X-CSRF-Token)中,拦截器同样会识别并校验。

自定义Servlet过滤器的正确实现(解决过滤器瓶颈)

如果你的Struts2版本过低不支持原生拦截器,自定义过滤器时要注意几个关键要点,避免踩坑:

  1. 过滤器顺序必须在Struts2调度器之前
    在web.xml中,你的CSRF过滤器要放在Struts2的StrutsPrepareAndExecuteFilter前面,确保请求先经过CSRF校验再进入Struts2处理:
<filter>
    <filter-name>CsrfFilter</filter-name>
    <filter-class>com.yourcompany.web.filter.CsrfFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>CsrfFilter</filter-name>
    <url-pattern>/*</url-pattern>
    <dispatcher>REQUEST</dispatcher>
    <dispatcher>FORWARD</dispatcher>
</filter-mapping>

<!-- Struts2调度器放在后面 -->
<filter>
    <filter-name>struts2</filter-name>
    <filter-class>org.apache.struts2.dispatcher.filter.StrutsPrepareAndExecuteFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>struts2</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>
  1. 核心过滤器逻辑实现
    过滤器只需要校验修改状态的请求(POST/PUT/DELETE等),GET类安全请求可直接放行,同时支持表单参数和请求头两种token传递方式:
public class CsrfFilter implements Filter {
    private static final String CSRF_TOKEN_SESSION_KEY = "CSRF_TOKEN";
    private static final String CSRF_PARAM_NAME = "_csrf";
    private static final String CSRF_HEADER_NAME = "X-CSRF-Token";

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest req = (HttpServletRequest) request;
        HttpServletResponse res = (HttpServletResponse) response;
        String method = req.getMethod().toUpperCase();

        // 放行安全请求方法,首次访问时生成CSRF token并存入session
        if ("GET".equals(method) || "HEAD".equals(method) || "OPTIONS".equals(method)) {
            if (req.getSession().getAttribute(CSRF_TOKEN_SESSION_KEY) == null) {
                req.getSession().setAttribute(CSRF_TOKEN_SESSION_KEY, UUID.randomUUID().toString());
            }
            chain.doFilter(request, response);
            return;
        }

        // 校验CSRF token
        String sessionToken = (String) req.getSession().getAttribute(CSRF_TOKEN_SESSION_KEY);
        String requestToken = req.getParameter(CSRF_PARAM_NAME);
        // 优先从请求头获取(适配AJAX)
        if (requestToken == null) {
            requestToken = req.getHeader(CSRF_HEADER_NAME);
        }

        if (sessionToken == null || !sessionToken.equals(requestToken)) {
            res.sendError(HttpServletResponse.SC_FORBIDDEN, "无效的CSRF令牌");
            return;
        }

        chain.doFilter(request, response);
    }

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {}

    @Override
    public void destroy() {}
}
  1. 前端配合实现
    • JSP表单中添加隐藏域:<input type="hidden" name="_csrf" value="${sessionScope.CSRF_TOKEN}"/>
    • AJAX请求时在请求头中携带token:
var csrfToken = "${sessionScope.CSRF_TOKEN}";
$.ajax({
    url: "/your-action",
    type: "POST",
    headers: {
        "X-CSRF-Token": csrfToken
    },
    // 其他请求参数
});

额外补充防护措施

  • Referer头校验:在过滤器中增加Referer校验,确保请求来自可信域名(注意:部分浏览器可能不发送Referer,不能作为唯一防护手段)
  • 敏感操作二次验证:对转账、修改密码等核心操作,额外添加验证码或密码确认步骤,提升安全性
  • 尽量升级Struts2版本:老旧版本不仅可能缺少原生CSRF防护,还存在其他已知安全漏洞,升级到稳定版本能从根源减少风险

内容的提问来源于stack exchange,提问作者Varun Pattar

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.15 13:52:49