`
JavaCrazyer
  • 浏览: 2990958 次
  • 性别: Icon_minigender_1
  • 来自: 河南
社区版块
存档分类

Struts温习(10)--利用Token解决重复提交

阅读更多

平时网上注册大家都经历过,比如用户在注册成功之后,再打击后退按钮,退回到表单页再次提交表单,如果未处理重复提交这一细节上的要求,将会再次成功提交,数据库中有重复数据,在一个良好的程序中式不允许这么出现的。

 Struts的Token(令牌)机制能够很好的解决表单重复提交的问题。

 基本原理是:服务器在处理到达的请求之前,会将请求中包含令牌值与保存在当前会话中的令牌值进行比较,看是否匹配。在处理完该请求后,且在相应发送给客户端之前,将会产生一个新的令牌值,该令牌值除传给客户端以外,也会将用户会话中的令牌值进行替换,这样如果用户后退到刚才的提交页面并再次提交的话,客户端传过来的令牌就和服务器端的令牌不一致,从而有效的防止了重复提交的发生

用户在使用时要注意一下两点:

第一、用户需要在请求中包含这个令牌值,请求中的令牌值如何保存,其实就和平时在页面中保存一些信息是一样的,通过隐藏域来保存,保存的形式如:<input type="hidden" name="org.apache.struts.taglib.html.TOKEN" value="6aa35341f25184fd996c4c918255c3ae" >,这个value是TokenProcessor类中的generateToken()获得的,是根据当前用户的session id 和当前时间的long值计算的

第二、在客户端提交后,要判断在请求中包含的值是否和服务器的令牌一直,因为服务器每次提交都会生成新的Token,所以,如果是重复提交,客户端的Token值和服务器端的Token值就会不一致


示例

index.jsp


<%@ page pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <title>Struts应用:利用Token防止重复提交</title>
  </head>
  
  <body>
    <h3>利用Token防止重复提交</h3><hr/>
    <a href="user.do?method=toAdd">添加用户</a>
  </body>
</html>

 addUser.jsp


<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@ taglib uri="http://struts.apache.org/tags-html" prefix="html" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <title>添加用户</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">
	<!--
	<link rel="stylesheet" type="text/css" href="styles.css">
	-->

  </head>
  
  <body>
  	<h3>添加用户</h3>
    <html:form action="user.do?method=add" method="post">
    	<table border="1" width="600px">
    		<tr>
    			<td>登录名</td>
    			<td><input type="text" name="loginname"/></td>
    		</tr>
    		<tr>
    			<td>密码</td>
    			<td><input type="password" name="pwd"/></td>
    		</tr>
    		<tr>
    			<td colspan="2" align="center">
    				<input type="submit" value="提交"/>&nbsp;
    				<input type="reset" value="重置"/>
    			</td>
    		</tr>
    	</table>
    </html:form>
  </body>
</html>

 login_success.jsp


<%@ page pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <title>登录成功</title>
  </head>
  
  <body>
    <h3>登录成功</h3><hr/>
   	<h2>欢迎:${param.loginname}登录!</h2> 
  </body>
</html>

 login_failure.jsp


<%@ page pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <title>登录失败</title>
  </head>
  
  <body>
    <h3>登录失败</h3><hr/>
   	<h2 style="color:red">可能的原因是:${errorMsg}</h2> 
  </body>
</html>

 LoginForm.java


package com.javacrazyer.web.formbean;

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 LoginForm extends ActionForm {
	
	public ActionErrors validate(ActionMapping mapping,
			HttpServletRequest request) {
		return null;
	}

	private static final long serialVersionUID = 6619272689058619128L;

	private String loginname;
	
	private String pwd;

	public String getLoginname() {
		return loginname;
	}

	public void setLoginname(String loginname) {
		this.loginname = loginname;
	}

	public String getPwd() {
		return pwd;
	}

	public void setPwd(String pwd) {
		this.pwd = pwd;
	}
}

 LoginAction.java


package com.javacrazyer.web.action;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
import org.apache.struts.actions.DispatchAction;

/**
 * 业务控制器:控制业务处理的流程
 *
 */
public class LoginAction extends DispatchAction {

	//具体业务流程处理方法,由Struts框架回调
	public ActionForward toAdd(ActionMapping mapping, ActionForm form,
			HttpServletRequest request, HttpServletResponse response)
			throws Exception {
		//产生Token值并存储到session中
		this.saveToken(request);
		
		return mapping.findForward("toadd");
	}
	
	public ActionForward add(ActionMapping mapping, ActionForm form,
			HttpServletRequest request, HttpServletResponse response)
			throws Exception {
		
		if(this.isTokenValid(request)){
			this.saveToken(request);
			System.out.println("往数据库添加数据...");
			
			return mapping.findForward("succ");
		}else{
			
			System.out.println("请不要重复提交数据.....");
			return mapping.findForward("failure");
		}
		
	}
	
}

 WEB-INF/struts-config.xml


<?xml version="1.0" encoding="UTF-8" ?>

<!DOCTYPE struts-config PUBLIC
          "-//Apache Software Foundation//DTD Struts Configuration 1.2//EN"
          "http://jakarta.apache.org/struts/dtds/struts-config_1_2.dtd">

<struts-config>

<!-- ================================================ Form Bean Definitions
-->
    <form-beans>
		<form-bean name="loginForm" type="com.javacrazyer.web.formbean.LoginForm"/>
    </form-beans>

<!-- =========================================== Action Mapping Definitions
-->
    <action-mappings>
		<action path="/user" name="loginForm" type="com.javacrazyer.web.action.LoginAction"
			parameter="method">
			<forward name="toadd" path="/adduser.jsp"/>
			<forward name="succ" path="/login_success.jsp"/>
			<forward name="failure" path="/login_failure.jsp"/>
		</action>
    </action-mappings>
</struts-config>

 具体页面的流程为



 

 这时如果点击后退那个箭头,再提交的话那么就是重复提交了,控制台多出现一条‘请不要重复提交数据’的输出信息


解释下这个流程:

首先是点击添加用户,跳到登录页面,这个过程调用了LoginAction.java中的toadd方法,这个方法中正好有个

saveToken(request)方法,那么这就说明了要将这步请求的TOKEN值保存在会话中,这样跳到登陆页面的话,登录页面会多出一个隐藏表单类似于上面说的<input type="hidden" name="org.apache.struts.taglib.html.TOKEN" value="6aa35341f25184fd996c4c918255c3ae" >,点击登录后那么就会出现将登录页面请求中包含的TOKEN值跟会话中的TOKEN值对比的情况,如果通过就提交成功。这样处理过后那么就会产生新的令牌值,会话中的令牌值也将改变,因此当点击最后页面的后退在提交时,页面的TOKEN值与会话中的已经不一样了,所以就避免了重复提交

  • 大小: 11.5 KB
  • 大小: 13.7 KB
  • 大小: 12.2 KB
  • 大小: 8.4 KB
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics