Struts 6.2.0中CSRF令牌实现遇invalid.token错误,求解决方案
解决Struts 6.2.0 CSRF防护返回invalid.token的问题
核心问题分析
你的配置存在几个关键问题,导致token验证失败:
- 拦截器栈配置无效:struts.xml中定义的
defaultSecurityStack是空栈,既没有包含params、workflow等基础拦截器,也未整合token拦截器,导致Action执行时缺少必要的上下文处理。 - 拦截器引用冲突且顺序错误:
TokenInterceptor和TokenSessionStoreInterceptor是互斥使用的,不能同时添加到Action的拦截器引用中;前者验证失败直接返回invalid.token,后者会将请求暂存到session中避免重复提交,同时处理token验证逻辑。 - 全局异常映射覆盖token拦截器默认结果:
TokenInterceptor验证失败会返回invalid.token结果,但你的全局异常映射将所有Exception都导向错误页,导致无法正确回到登录页重新提交。
修正方案
1. 修复struts.xml拦截器栈配置
将基础拦截器和token拦截器整合到默认栈,确保所有Action继承正确的拦截器逻辑:
<package name="struts-security" abstract="true" extends="struts-default"> <interceptors> <!-- 整合默认栈与tokenSessionStore拦截器 --> <interceptor-stack name="defaultSecurityStack"> <interceptor-ref name="defaultStack"/> <interceptor-ref name="tokenSessionStore"/> </interceptor-stack> </interceptors> <default-interceptor-ref name="defaultSecurityStack"/> <global-results> <result name="error">/error401.jsp</result> <!-- 添加token验证失败的全局结果,指向登录页 --> <result name="invalid.token" type="tiles">login.Login</result> </global-results> <global-exception-mappings> <!-- 缩小异常范围,避免覆盖token拦截器的结果处理 --> <exception-mapping result="error" exception="java.lang.RuntimeException"/> </global-exception-mappings> </package>
2. 简化LoginAction的拦截器配置
移除重复的拦截器引用,因为默认栈已包含tokenSessionStore:
@Namespace("/common") @Action("Login") @Results({ @Result(name = "input", location = "login.Login", type = "tiles"), // 可选:单独定义当前Action的token失败结果 @Result(name = "invalid.token", location = "login.Login", type = "tiles") }) public class LoginAction extends ActionSupport { // 业务逻辑代码 }
3. 确认login.jsp表单配置正确
确保s:form的action属性正确指向LoginAction,s:token标签在表单内部(现有代码已满足,补充完整示例):
<%@ taglib prefix="s" uri="/struts-tags"%> <s:form action="Login" method="post" validate="true" theme="simple" name="form" id="paraFrm"> <s:token /> <!-- 其他表单字段 --> <s:textfield name="username" label="用户名"/> <s:password name="password" label="密码"/> <s:submit value="登录"/> </s:form>
4. 可选:优化Token行为配置
在struts.properties中添加以下参数,调整token的存储和过期规则:
# token的session存储键名 struts.token.name=token # token过期时间(毫秒,此处为1小时) struts.token.session.expiration=3600000
关键注意事项
- 不要同时使用
TokenInterceptor和TokenSessionStoreInterceptor:推荐用TokenSessionStoreInterceptor处理重复提交,若需严格验证token,单独使用TokenInterceptor并处理invalid.token结果。 - 确保Session正常工作:token依赖Session存储,若Session失效或未正确初始化,会导致验证失败。
- 必须通过Struts标签生成表单:
s:token会自动渲染隐藏字段并绑定Session中的token值,手动写HTML表单无法生成有效token。
内容的提问来源于stack exchange,提问作者SHIVAM AGRAHARI
相关产品推荐
相关产品推荐

