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

非Spring环境下Java EE JSF应用CSRF防护实现方案咨询

JSF + PrimeFaces 无Spring依赖CSRF防护落地方案

JSF 2.2及以上版本本身内置了基于ViewState的CSRF防护能力,和PrimeFaces组件全版本兼容,不需要额外引入OWASP CSRF Guard或者Spring生态组件,零额外依赖就能满足防护要求。如果使用JSF 2.2以下的老版本,也可以通过简单的自定义过滤器实现同等防护能力。

基础防护配置(JSF 2.2+ 适用)

首先在项目web.xml中确认/添加如下配置,打开默认的防护开关,不要手动关闭ViewState校验:

<!-- ViewState存在服务端,避免令牌被客户端篡改 -->
<context-param>
    <param-name>javax.faces.STATE_SAVING_METHOD</param-name>
    <param-value>server</param-value>
</context-param>
<!-- 显式开启视图参数保护,默认JSF2.2+为true,避免被其他配置覆盖 -->
<context-param>
    <param-name>javax.faces.PROTECT_VIEW_PARAMETERS</param-name>
    <param-value>true</param-value>
</context-param>
<!-- 配置所有涉及数据修改的受保护视图路径,这些路径的非GET请求会强制校验ViewState -->
<context-param>
    <param-name>javax.faces.PROTECTED_VIEWS</param-name>
    <param-value>/admin/*,/user/secure/*,/order/*,/api/modify/*</param-value>
</context-param>

配置完成后,所有受保护路径的POST请求、PrimeFaces发起的AJAX请求,JSF都会自动校验请求携带的javax.faces.ViewState参数和当前会话绑定的服务端存储值是否匹配,不匹配会直接抛出ProtectedViewException拒绝请求。PrimeFaces的p:commandButton、p:ajax等组件默认会自动在请求中携带ViewState参数,不需要额外开发前端传参逻辑。

注意:不要在PrimeFaces组件上设置process="@none"且不提交ViewState,会导致校验失败。如果有自定义的原生AJAX请求,从页面DOM中提取ViewState值携带即可:

// 自定义请求携带JSF原生CSRF令牌
const viewState = document.querySelector('input[name="javax.faces.ViewState"]').value;
fetch('/custom/submit', {
    method: 'POST',
    headers: {
        'Faces-Request': 'partial/ajax'
    },
    body: new URLSearchParams({
        'javax.faces.ViewState': viewState,
        'bizParam': 'value'
    })
})

兼容补充方案(JSF 2.2以下版本/非JSF接口适用)

如果项目使用JSF 2.0/2.1等不支持内置受保护视图的老版本,或者项目中存在独立Servlet、REST接口等不走JSF生命周期的请求,不需要集成OWASP CSRF Guard,写一个简单的Servlet Filter即可实现CSRF校验,总代码量不超过100行,逻辑完全可控:

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.util.UUID;

@WebFilter("/*")
public class CsrfValidationFilter implements Filter {
    private static final String CSRF_TOKEN_SESSION_KEY = "APP_CSRF_TOKEN";
    private static final String CSRF_TOKEN_HEADER = "X-CSRF-TOKEN";

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

        // GET/HEAD/OPTIONS请求不涉及数据修改,跳过校验,首次访问时生成令牌
        if ("GET".equals(method) || "HEAD".equals(method) || "OPTIONS".equals(method)) {
            if (session.getAttribute(CSRF_TOKEN_SESSION_KEY) == null) {
                String token = UUID.randomUUID().toString().replace("-", "");
                session.setAttribute(CSRF_TOKEN_SESSION_KEY, token);
            }
            chain.doFilter(request, response);
            return;
        }

        // POST/PUT/DELETE等修改类请求校验令牌
        String expectedToken = (String) session.getAttribute(CSRF_TOKEN_SESSION_KEY);
        String actualToken = req.getHeader(CSRF_TOKEN_HEADER);
        if (actualToken == null) {
            actualToken = req.getParameter(CSRF_TOKEN_SESSION_KEY);
        }

        if (expectedToken == null || !expectedToken.equals(actualToken)) {
            resp.sendError(HttpServletResponse.SC_FORBIDDEN, "CSRF校验失败");
            return;
        }
        chain.doFilter(request, response);
    }
}

前端通过JSF EL表达式直接取出Session中存储的令牌,配置全局AJAX请求自动携带即可:

// 全局配置所有AJAX请求自动带CSRF令牌
const csrfToken = "#{sessionScope.APP_CSRF_TOKEN}";
// fetch全局拦截
const originalFetch = window.fetch;
window.fetch = function(url, options = {}) {
    options.headers = options.headers || {};
    options.headers[CSRF_TOKEN_HEADER] = csrfToken;
    return originalFetch(url, options);
}
// 如果项目用jQuery,加一行全局配置即可
$.ajaxSetup({
    headers: { 'X-CSRF-TOKEN': csrfToken }
});

方案优势

  • 零额外依赖:不需要引入Spring、OWASP CSRF Guard等第三方组件,完全基于Java EE原生API实现,不会出现依赖冲突
  • 兼容性好:JSF内置ViewState校验和PrimeFaces生命周期完全适配,不会出现第三方过滤器拦截AJAX请求导致组件失效的问题
  • 逻辑可控:自定义Filter代码量极小,可以根据业务需求灵活配置拦截路径、校验规则,不需要额外学习复杂的第三方配置规则

有效性验证

配置完成后可以通过两个简单步骤确认防护生效:

  • 构造跨域测试页面,写一个不带ViewState/CSRF令牌的POST表单提交到受保护接口,确认返回403状态码
  • 抓包查看正常业务请求,确认每个POST/AJAX请求都携带了和当前会话绑定的令牌,且不同会话的令牌值不重复

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 10:18:21