重复提交问题在web开发中总会遇到,他主要发生在对数据库进行插入操作时。主要分为两种,一种是点击浏览器的刷新页面的重复提交,另一种是后退到上一页面,然后重新点击提交的重复提交。无论上面的哪一种,struts的token机制都能够很好的解决。
其基本原理是:服务器端在处理到达的请求之前,会将请求中包含的token值与保存在当前用户会话中的token值进行比较,看是否匹配。在处理完该请求后,且在答复发送给客户端之前,将会产生一个新的token,该token除传给客户端以外,也会将用户会话中保存的旧的token进行替换。这样如果用户回退到刚才的提交页面并再次提交的话,客户端传过来的token就和服务器端的token不一致,从而有效地防止了重复提交的发生。
下面给出一个简单例子,该例子中只对数据库进行模拟操作,主要是在Struts端进行讲解。
1. 在MyEclipse中创建一个名为struts的工程,导入struts1.x包。
2. 创建3个jsp页面——index.jsp,success.jsp,add.jsp,failure.jsp。其中index.jsp可以利用工程自带的,只需将其修改为:
<%@ page language="java" pageEncoding="utf-8" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>My JSP 'index.jsp' starting page</title>
<meta http-equiv="pragma" content="no-cache">
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="expires" content="0">
<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
<meta http-equiv="description" content="This is my page">
</head>
<body>
<a href ="toAdd.do">添加</a>
</body>
</html>
add.jsp的页面代码为,这个页面有一点很重要,就是form标签,一定要使用struts的form标签,如果不使用<html:form>,而是使用html自己的<form>,就不能够正常添加token,因为token只对struts的html标签支持,所以如题称为struts的token机制,其他标签可以不适用struts的标签:
-----------------------------------------------------------------------------------------------------------------
<%@ page language="java" pageEncoding="utf-8" %>
<%@ taglib uri="http://struts.apache.org/tags-bean" prefix="bean" %>
<%@ taglib uri="http://struts.apache.org/tags-html" prefix="html" %>
<%@ taglib uri="http://struts.apache.org/tags-logic" prefix="logic" %>
<%@ taglib uri="http://struts.apache.org/tags-tiles" prefix="tiles" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html:html >
<head>
<title>add.jsp</title>
</head>
<body>
<html:form action="/add">
<input type="text" id="username" name="username"/><br>
<input type="password" id="password"/ name="password"><br>
<input type="submit" value="提交" >
</html:form>
</body>
</html:html>
-----------------------------------------------------------------
接下来分别是success.jsp和failure.jsp的页面代码,其实都很简单,但是为了完整吧,写出来了:
---------------------------------------------------------------------------------------------------------
<%@ page language="java" pageEncoding="utf-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>success.jsp</title>
<meta http-equiv="pragma" content="no-cache">
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="expires" content="0">
<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
<meta http-equiv="description" content="This is my page">
</head>
<body>
添加成功!!<br>
<a href ="toAdd.do">继续添加</a>
</body>
</html >
----------------------------------------------------------------
<%@ page language="java" pageEncoding="utf-8" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>failure.jsp</title>
<meta http-equiv="pragma" content="no-cache">
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="expires" content="0">
<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
<meta http-equiv="description" content="This is my page">
</head>
<body>
添加失败!!!你是不是重复提交啦?
</body>
</html>
--------------------------------------------------------------------------------------------------------------
3. 然后是添加2个action——ToAddAction.java,AddAction.java
--------------------------------------------------------------------------------------------------------------------
package com.yourcompany.struts.action;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.struts.action.Action;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
public class ToAddAction extends Action {
public ActionForward execute(ActionMapping mapping, ActionForm form,
HttpServletRequest request, HttpServletResponse response) {
saveToken(request);//此处进行保存,即可在下一页面add.jsp中传送token值
System.out.println();
return mapping.findForward("add");
}
}
---------------------------------------------------------------------------------------------------------------------
package com.yourcompany.struts.action;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.struts.action.Action;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
import org.apache.struts.action.ActionMessage;
import org.apache.struts.action.ActionMessages;
import com.lc.sqlhelp.Access;
import com.yourcompany.struts.form.AddForm;
public class AddAction extends Action {
public ActionForward execute(ActionMapping mapping, ActionForm form,
HttpServletRequest request, HttpServletResponse response) {
AddForm af = (AddForm) form;
if (isTokenValid(request, true)) {
String sql = "insert into user (username,password) values('"
+ af.getUsername() + "','" + af.getPassword() + "')";
System.out.println(sql);
return mapping.findForward("add_suc");
}
saveToken(request);
return mapping.findForward("add_fail");
}
}
---------------------------------------------------------------------------------------------------------------------
4. 还有对应的formAction——AddForm.java
package com.yourcompany.struts.form;
import javax.servlet.http.HttpServletRequest;
import org.apache.struts.action.ActionErrors;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionMapping;
public class AddForm extends ActionForm {
/** password property */
private String password;
/** username property */
private String username;
/*
* Generated Methods
*/
public ActionErrors validate(ActionMapping mapping,
HttpServletRequest request) {
// TODO Auto-generated method stub
return null;
}
public void reset(ActionMapping mapping, HttpServletRequest request) {
// TODO Auto-generated method stub
}
/**
* Returns the password.
* @return String
*/
public String getPassword() {
return password;
}
/**
* Set the password.
* @param password The password to set
*/
public void setPassword(String password) {
this.password = password;
}
/**
* Returns the username.
* @return String
*/
public String getUsername() {
return username;
}
/**
* Set the username.
* @param username The username to set
*/
public void setUsername(String username) {
this.username = username;
}
}
5. 然后是struts-config.xml,要注意struts的版本,我用的是struts1.2:
<?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>
<form-beans >
<form-bean name="addForm" type="com.yourcompany.struts.form.AddForm" />
</form-beans>
<global-exceptions />
<global-forwards />
<action-mappings >
<action
path="/toAdd"
type="com.yourcompany.struts.action.ToAddAction">
<forward name="add" path="/add.jsp" />
</action>
<action
attribute="addForm"
input="/add.jsp"
name="addForm"
path="/add"
scope="request"
type="com.yourcompany.struts.action.AddAction">
<forward name="add_suc" path="/success.jsp" />
<forward name="add_fail" path="/failure.jsp" />
</action>
</action-mappings>
<message-resources parameter="com.yourcompany.struts.ApplicationResources" />
</struts-config>
6. 最后部署到tomcat中,运行程序http://localhost:8080/struts,,点击添加链接,即可进入http://localhost:8080/struts/toAdd.do,如果你在IE浏览器或者Firefox中进行测试的话,此时查看当前页面源代码,会发现其中body中的代码如下:
<body>
<form name="addForm" method="post" action="/struts/add.do"><div><input type="hidden" name="org.apache.struts.taglib.html.TOKEN" value="3934d9a3c0dcc57574411d5095649909"></div>
<input type="text" id="username" name="username"/><br>
<input type="password" id="password"/ name="password"><br>
<input type="submit" value="提交">
</form>
</body>
其中,form中多了一句隐藏,<input type="hidden" name="org.apache.struts.taglib.html.TOKEN" value="3934d9a3c0dcc57574411d5095649909">,这个就是在saveToken(request)之后的结果,如果没有这句话,就不会产生这个隐藏的token,这样再add.do中才能够对token的值进行比较,判断是否重复提交。
7. 添加信息后,点击浏览器的刷新按钮或者后退到添加信息页面进行再次提交,都会进入failure.jsp页面,重复提交解决。