如果网络不好,提交数据后可能就没有反应了。这时候用户不知道数据是否已提交到服务器了,往往还会单击提交按钮再提交一次。对于某些应用,例如网络购物,银行转账等,重复提交会导致非常严重的后果。
解决这个问题可以采用箭牌令(token) 原理是这样的: 客户请求某个页面时如A.jsp。 web服务器在服务端和客户端同时都放了一个箭牌令,这两个值是相同的。当A.jsp提交时 将A.jsp中所带的箭牌令和服务端的箭牌令相比较。如果相同则表示是第一次提交,在返回前修改服务端的箭牌令德值。当A.jsp由于刷新,或其他原因再次提交时,在服务端任就将A.jsp带的箭牌令与服务端的箭牌令比较,这时由于服务端的箭牌令已被修改。所以两者值不同。因此可以判断是重复提交。
看看struts1如何处理
用户请求录入页面,这个与服务器建立的一次连接过程中,
在服务器端①【生成一个session标识,同时返回到客户端一个与此匹配的hidden域】。
用户提交了此页面,服务器端首先②【判断此hidden域与session标识是否匹配】,
若不匹配,终止保存操作,提示同一表单不能提交两次,
同时①【新建一个session标识和hidden域】,返回录入页面;
若匹配,执行插入保存操作,同时③【清空(重置reset)session标识】。
Struts正在基于这样的思路在org.apache.struts.action.Action类中提供了内置支持方法:
protected void saveToken(HttpServletRequest request) 配合标签对应于①
protected boolean isTokenValid(HttpServletRequest request) 对应于②
protected void resetToken(HttpServletRequest request) 对应于③
注意:
1.token.jsp必须用html:form标签
记住一点,Struts在你每次访问Action的时候,都会产生一个令牌,保存在你的Session里面,如果你在Action里的函数里面,使用了saveToken(request);,那么这个令牌也会保存在这个Action所Forward到的jsp所生成的静态页面里。
如果你在你Action的方法里使用了isTokenValid,那么Struts会将你从你的request里面去获取这个令牌值,然后和Session里的令牌值做比较,如果两者相等,就不是重复提交,如果不相等,就是重复提交了。
当然你也可以在你的action所继承的父类里面做表单重复提交的控制,这样就可以不用在所有的子类里来分别再写防止重复提交的代码
index.jsp
- <%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
- <%@ taglib uri="http://jakarta.apache.org/struts/tags-html" prefix="html"%>
- <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
- <html>
- <head>
- <title>My JSP 'index.jsp' starting page</title>
- </head>
- <body>
- <html:link action="token.do?method=token">Token</html:link>
- </body>
- </html>
- TokenAction.java
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@ taglib uri="http://jakarta.apache.org/struts/tags-html" prefix="html"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>My JSP 'index.jsp' starting page</title>
</head>
<body>
<html:link action="token.do?method=token">Token</html:link>
</body>
</html>
TokenAction.java
- public class TokenAction extends DispatchAction {
- public ActionForward token(ActionMapping mapping, ActionForm form,
- HttpServletRequest request, HttpServletResponse response) {
- TokenForm tokenForm = (TokenForm) form;
- //saveToken创建一个新令牌,如果没有调用此方法,那么页面不会生成隐藏标签
- this.saveToken(request);
- return mapping.findForward("token");
- }
- public ActionForward add(ActionMapping mapping, ActionForm form,
- HttpServletRequest request, HttpServletResponse response) {
- TokenForm tokenForm = (TokenForm) form;
- //如果为假,则进行了重复提交
- if(isTokenValid(request,true)){
- System.out.println("正常提交");
- this.resetToken(request);
- }else{
- System.out.println("重复提交");
- //重新生成令牌
- this.saveToken(request);
- return mapping.getInputForward();
- }
- return mapping.findForward("token");
- }
- }
public class TokenAction extends DispatchAction {
public ActionForward token(ActionMapping mapping, ActionForm form,
HttpServletRequest request, HttpServletResponse response) {
TokenForm tokenForm = (TokenForm) form;
//saveToken创建一个新令牌,如果没有调用此方法,那么页面不会生成隐藏标签
this.saveToken(request);
return mapping.findForward("token");
}
public ActionForward add(ActionMapping mapping, ActionForm form,
HttpServletRequest request, HttpServletResponse response) {
TokenForm tokenForm = (TokenForm) form;
//如果为假,则进行了重复提交
if(isTokenValid(request,true)){
System.out.println("正常提交");
this.resetToken(request);
}else{
System.out.println("重复提交");
//重新生成令牌
this.saveToken(request);
return mapping.getInputForward();
}
return mapping.findForward("token");
}
}
token.jsp
- <%@ page language="java" pageEncoding="UTF-8"%>
- <%@ taglib uri="http://jakarta.apache.org/struts/tags-bean" prefix="bean"%>
- <%@ taglib uri="http://jakarta.apache.org/struts/tags-html" prefix="html"%>
- <html>
- <head>
- <title>JSP for TokenForm form</title>
- </head>
- <body>
- <html:form action="/token.do?method=add">
- 查看源代码可以看到隐藏标签,隐藏域value值的"32位随机码<br>
- 姓名: <html:password property="username"/><br/>
- 密码 : <html:text property="password"/><br/>
- <html:submit/><html:cancel/>
- </html:form>
- </body>
- </html>
<%@ page language="java" pageEncoding="UTF-8"%>
<%@ taglib uri="http://jakarta.apache.org/struts/tags-bean" prefix="bean"%>
<%@ taglib uri="http://jakarta.apache.org/struts/tags-html" prefix="html"%>
<html>
<head>
<title>JSP for TokenForm form</title>
</head>
<body>
<html:form action="/token.do?method=add">
查看源代码可以看到隐藏标签,隐藏域value值的"32位随机码<br>
姓名: <html:password property="username"/><br/>
密码 : <html:text property="password"/><br/>
<html:submit/><html:cancel/>
</html:form>
</body>
</html>
struts-config.xml
- <?xml version="1.0" encoding="UTF-8"?>
- <!DOCTYPE struts-config PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 1.2//EN" "http://struts.apache.org/dtds/struts-config_1_2.dtd">
- <struts-config>
- <data-sources />
- <form-beans >
- <form-bean name="tokenForm" type="com.dahai.struts.form.TokenForm" />
- </form-beans>
- <global-exceptions />
- <global-forwards />
- <action-mappings >
- <action
- attribute="tokenForm"
- input="/token.jsp"
- name="tokenForm"
- parameter="method"
- path="/token"
- scope="request"
- type="com.dahai.struts.action.TokenAction" >
- <forward name="token" path="/token.jsp"/>
- </action>
- </action-mappings>
- <message-resources parameter="com.dahai.struts.ApplicationResources" />
- </struts-config>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE struts-config PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 1.2//EN" "http://struts.apache.org/dtds/struts-config_1_2.dtd">
<struts-config>
<data-sources />
<form-beans >
<form-bean name="tokenForm" type="com.dahai.struts.form.TokenForm" />
</form-beans>
<global-exceptions />
<global-forwards />
<action-mappings >
<action
attribute="tokenForm"
input="/token.jsp"
name="tokenForm"
parameter="method"
path="/token"
scope="request"
type="com.dahai.struts.action.TokenAction" >
<forward name="token" path="/token.jsp"/>
</action>
</action-mappings>
<message-resources parameter="com.dahai.struts.ApplicationResources" />
</struts-config>
再来看看struts2如何处理
以TokenAction为例
TokenAction中有一个names属性,用来存储提交过的所有的数据。重复提交数据时,如果能提交进来,names中将会积累重复的数据,以此来判断数据是否被重复提交。
package com.helloweenvsfei.strutes.action;
public class TokenAction extends ActionSupport{
private String name;
public String execute(){
names.add(name);
return Action.SUCCESS;
}
public void setName(String name) {
this.name = name;
}
public static List<String> getNames() {
return names;
}
}
struts.xml:
<struts>
<!-- Include webwork default (from the Struts JAR). -->
<include file="struts-default.xml"/>
<!-- Configuration for the default package. -->
<package name="default" extends="struts-default">
<!-- Default interceptor stack. -->
<default-interceptor-ref name="paramsPrepareParamsStack"/>
<action name="token" class="action.TokenAction">
<interceptor-ref name="token" /> <!-- token拦截器 -->
<interceptor-ref name="basicStack"/>
<result name="success">/jsp/tokenSuccess.jsp</result>
<result name="input">/jsp/tokenInput.jsp</result>
<result name="invalid.token">/jsp/tokenInvalid.jsp</result>
</action>
</package>
</struts>
提交页面
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<%@ taglib uri="/struts-tags" prefix="struts" %>
<struts:form action="token">
<struts:token /> <!-- token拦截器 -->
<struts:label>避免重复输入</struts:label>
<struts:textfield name="name" label="请输入姓名" />
<struts:submit name="提交"/>
</struts:form>