如何在Struts2应用程序中为AJAX请求实现CSRF保护。
问题的原因是在Struts2应用程序中,对于AJAX请求缺乏跨站请求伪造(CSRF)保护。作者通过为AJAX请求生成令牌,并将其与正常响应一起发送来解决这个问题。他计划将此过程抽象为一个实用方法,对于需要重复执行而无需刷新页面的操作,将该方法作为响应的一部分返回。作者仍在寻找一个更优雅的解决方案。
以下是作者提供的代码示例:
Map<String, String> tokenInfo = Maps.newHashMap(); tokenInfo.put("struts.token.name", TokenHelper.getTokenName()); tokenInfo.put(TokenHelper.getTokenName(), TokenHelper.setToken());
作者还表示他们也遇到了这个问题,并询问是否有更好的解决方案。同时,他们还询问作者的解决方案是否对JSP文件进行了任何更改。
问题的原因是需要在Struts2应用程序中为AJAX请求实现CSRF保护,并且还需要防止重复提交。
解决方法是生成一个令牌,并将其作为参数在ajax调用中使用。对于后续的AJAX调用,可以使用一个“每个用户”的令牌,不一定是“每个请求”的令牌。这在Stack Overflow的一个答案中有解释。然而,这种方法对于防止重复提交并不是非常有效。
以下是一种可能的解决方案:
public class CsrfInterceptor extends AbstractInterceptor { @Override public String intercept(ActionInvocation invocation) throws Exception { HttpServletRequest request = ServletActionContext.getRequest(); HttpServletResponse response = ServletActionContext.getResponse(); // Check if it's an AJAX request if ("XMLHttpRequest".equals(request.getHeader("X-Requested-With"))) { String token = (String) request.getSession().getAttribute("csrfToken"); String requestToken = request.getParameter("csrfToken"); // Check if the token matches if (token == null || !token.equals(requestToken)) { response.sendError(HttpServletResponse.SC_FORBIDDEN, "CSRF token validation failed"); return null; } } // Continue with the action return invocation.invoke(); } }
在Struts2配置文件中,将此拦截器添加到适当的位置:
在首次加载页面时,生成一个CSRF令牌,并将其存储在会话中:
public class MyAction extends ActionSupport { @Override public String execute() throws Exception { HttpServletRequest request = ServletActionContext.getRequest(); HttpSession session = request.getSession(); // Generate a CSRF token String token = UUID.randomUUID().toString(); // Store the token in the session session.setAttribute("csrfToken", token); return SUCCESS; } }
在AJAX请求中,将令牌作为参数发送:
var csrfToken = "${sessionScope.csrfToken}"; $.ajax({ url: "ajaxUrl", type: "POST", data: { csrfToken: csrfToken, // Other parameters }, success: function(response) { // Handle success }, error: function(xhr, status, error) { // Handle error } });
这样,每次发起AJAX请求时,都会检查令牌是否匹配。如果令牌不匹配,将返回403 Forbidden错误。这样可以防止CSRF攻击,并且还可以防止重复提交。