Struts2中重复提交表单分析
来源:互联网 发布:淘宝账号贷款怎么操作 编辑:程序博客网 时间:2024/06/06 22:01
原因:Struts2提交表单完成添加数据等操作后,再去刷新页面会弹出警告,提示信息会再次被提交(同样的表单数据)
解决:在action中配置拦截器
1.需要在提交数据的表单<form>
内增加<s:token></s:token>
在jsp的from标签里加入<s:token/>
防重复提交标签,<s:token/>
生成如下的内容:(struts.token.name 标识哪个隐藏域存了 token 值)
注意:按页面的刷新按钮是刷新上一次请求,再次提交是新一次请求 所以即使提交了转到新的页面,再按刷新也是上一次请求,会重复提交数据(token值是旧的,session中与之对应的值已经被删除),而按form里面的提交按钮是新的请求(不会重复提交表单,会提交新的表单token值是新的)
<input type="hidden" name="struts.token.name" value="struts.token"/> <input type="hidden" name="struts.token" value="7GXL55LPSGU19SDC9D3VP54I20XT3BVA"/>
注意自定义的表单域别重名了。它的作用是防止表单重复提交,每次加载页面 struts.token 的值都不一样,如果两次提交时该值一样,则认为是重复提交。此时要启用 TokenInterceptor(token) 拦截器,最好是也启用 TokenSessionStoreInterceptor(token-session) 拦截器
2.action标签内配置拦截器
(1)可以在父类package的action标签内配置全局拦截器栈
<package name="allAccess" namespace="" extends="struts-default"> <interceptors> <!--定义拦截器栈--> <interceptor-stack name="myStack"> <!-- 需要在action的声明中,为action添加token拦截器,因为token拦截器不在defaultStack拦截器栈中, 注意,需要将拦截器放在拦截器栈的第一位,这是因为判断表单是否被重复提交的逻辑应该在表单处理前。 --> <interceptor-ref name="token"/> <interceptor-ref name="tokenSession"> <!--只拦截update方法--> <param name="includeMethods">update</param> </interceptor-ref> <interceptor-ref name="defaultStack"/> <!--调用默认拦截器--> </interceptor-stack> </interceptors> <default-interceptor-ref name="myStack"> </default-interceptor-ref><!--把默认的拦截器栈改为自定义的--> <global-results> <result name="login"> /Longin.jsp </result> <result name="main"> /Main.jsp </result> </global-results> </package>
(2)可以单独为一个action标签配置拦截器。
<struts> <!-- Add packages here --> <package name="teacher" namespace="/teacher" extends="allAccess"> <action name="*" class="action.TeacherAction" method="{1}"> <!-- 需要在action的声明中,为action添加token拦截器,因为token拦截器不在defaultStack拦截器栈中, 注意,需要将拦截器放在拦截器栈的第一位,这是因为判断表单是否被重复提交的逻辑应该在表单处理前。 --> <interceptor-ref name="token"/> <interceptor-ref name="tokenSession"></interceptor-ref> <interceptor-ref name="defaultStack"></interceptor-ref> <!-- 如果重复提交,不会跳转到error.jsp页面 --> <result name="main">/Teacheradmin.jsp</result> <result name="invalid.token">/error.jsp</result> </action> </package></struts>
注意:Struts2在防止表单重复提交的拦截有2个,token与tokenSession,tokenSession继承于token,当使用token时候需要额外加上表单重复提交跳转错误页面的result
token、token-session 和 defaultStack 的顺序要保证,还需要加上名为 “invalid.token” 的 result,当发现重复提交时转向到这个逻辑页,如 /error.jsp,在 /error.jsp 加上 <s:actionerror />
在出现重复提交时就会提示:The form has already been processed or no token was supplied, please try again.
<result name="invalid.token">/error.jsp</result>
tokenSession不需要加错误跳转页面,它直接不跳转。
后台如何对比session里面的token与前端的token的方法实现
/** * Checks for a valid transaction token in the current request params. If a valid token is found, it is * removed so the it is not valid again. * * @return false if there was no token set into the params (check by looking for {@link #TOKEN_NAME_FIELD}), true if a valid token is found */ public static boolean validToken() { String tokenName = getTokenName(); if (tokenName == null) { if (LOG.isDebugEnabled()) { LOG.debug("no token name found -> Invalid token "); } return false; } String token = getToken(tokenName); if (token == null) { if (LOG.isDebugEnabled()) { LOG.debug("no token found for token name "+tokenName+" -> Invalid token "); } return false; } Map session = ActionContext.getContext().getSession(); String tokenSessionName = buildTokenSessionAttributeName(tokenName); String sessionToken = (String) session.get(tokenSessionName); if (!token.equals(sessionToken)) { if (LOG.isWarnEnabled()) { LOG.warn(LocalizedTextUtil.findText(TokenHelper.class, "struts.internal.invalid.token", ActionContext.getContext().getLocale(), "Form token {0} does not match the session token {1}.", new Object[]{ token, sessionToken })); } return false; } // remove the token so it won't be used again session.remove(tokenSessionName); return true; }
主要是取得前端页面的token(通过token域指向token参数)的值
其次再到session里面利用token参数取得它的值
二者比较,相同则session里移除这个token的值
其次新的token生成依靠自带的token生成方法,并存储于session中,当新转向页面时标签回到session中取得token的值放在前端中以便下次和session中比较
token生成时值是存储在一个数组当中,每次生成一个token就存储在数组末端,当token匹配成功会删掉数组中第一个元素,以便后面元素向前收缩(索引),原本在第一个元素后面的元素的索引就变成了第一。
注意注意注意!
经过本人实践在自定义拦截器中,要加上拦截器所需要拦截的方法,不加不可以(适用于利用通配符配置action情况)
<interceptors> <!--定义自定义拦截器栈 --> <interceptor-stack name="myStack"> <!-- <interceptor-ref name="token"> <param name="includeMethods">save</param> </interceptor-ref>token或tokensession两个拦截器选其一--> <interceptor-ref name="tokenSession"> <param name="includeMethods">save,update</param> </interceptor-ref> <interceptor-ref name="defaultStack" /> </interceptor-stack> </interceptors> <default-interceptor-ref name="myStack" /> <!--引用自定义拦截器栈 -->
注意可以指定拦截多个方法,或不拦截多个方法
<!-- includeMethods表示包含指定的方法,即对标记为includeMethods的方法进行拦截 --> <param name="includeMethods">saveCinema,saveCinemaAndtoAddScreen,updateCinema</param> <!-- 定义被排除的方法名,也就是你action中不被这个拦截器拦截的方法名 --> <param name="excludeMethods"></param> -->
原理
让服务器生成一个唯一标记,并在服务器和表单里各保存一份这个标记的副本。此后,在用户提交表单的时候,表单里的标记将随着其他请求参数一起发送到服务器,服务器将对他收到的标记和它留存的标记进行比较。如果两者匹配,这次提交的表单被认为是有效的,在处理完该请求后,且在答复发送给客户端之前,将会产生一个新的令牌,该令牌除传给客户端以外,也会将用户会话中保存的旧的令牌进行替换。这样如果用户回退到刚才的提交页面并再次提交的话,客户端传过来的令牌就和服务器端的令牌不一致,从而有效地防止了重复提交的发生。
- Struts2中重复提交表单分析
- Struts2中防止表单重复提交
- Struts2中防止表单重复提交
- Struts2中防止表单的重复提交
- struts2表单重复提交
- 【struts2】struts防止表单重复提交源码分析
- Struts2防止表单重复提交
- Struts2防止表单重复提交
- Struts2防止表单重复提交
- struts2防止表单重复提交
- Struts2防止表单重复提交
- Struts2解决表单重复提交
- Struts2处理表单重复提交
- Struts2防止表单重复提交
- Struts2防止表单重复提交
- Struts2防止表单重复提交
- Struts2防止表单重复提交 .
- Struts2防止表单重复提交
- JAVA学习笔记(3)--Arrays类使用详解
- Java之AES加解密示例
- jquery控制div的显示与隐藏
- Ubuntu下切换python版本的方法
- CSS 子元素选择器
- Struts2中重复提交表单分析
- Spring Boot 2.0
- 解决MathType公式会大小不一的问题
- 2017/9/23 肖明大佬
- 用canvas实现简单的飞机大战游戏
- input_data.py解析
- 炸僵尸
- Email应用
- 字符流是否来自于转换流,为什么?