平时网上注册大家都经历过,比如用户在注册成功之后,再打击后退按钮,退回到表单页再次提交表单,如果未处理重复提交这一细节上的要求,将会再次成功提交,数据库中有重复数据,在一个良好的程序中式不允许这么出现的。
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="提交"/>
<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
分享到:
相关推荐
Struts 之旅 - 重复提交 token
利用Token机制解决重复重复提交
JavaEE Struts2利用tokenSession防止重复提交
mapbox-gl 2.7.0 去token
用struts的token解决重复提交问题
在struts 中可以通过token 来解决重复提交的问题。
struts2防止表单重复提交,利用struts的拦截器tokenSession,轻轻松松解决表单重复提交的问题。 附件为源代码,后台延迟了3秒,可直接在web服务器下部署运行,输入用户名和密码后,多点几次提交按钮,然后看控制台...
struts token机制解决表单重复提交
随着微服务的流行,服务和服务之间的稳定性变得越来越重要。Sentinel 以流量为切入点,从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性。
前端开源库-google-translate-tokengoogle translate token,一个生成免费使用google translate api所需token的包。
详细介绍struts+token机制解决表单重复提交问题。附带相关代码
视频部分 下载前请阅读博客文章详细 博文链接:https://dlivan.iteye.com/blog/69717
struts2_token控制刷新重复提交
1.3.0(2020-10-29) 修复获取路径中解析返回结果中多维数组的问题 支持 http url 使用自签名证书 1.2.3(2020-3-9) 修复获取路径中获取 header 简单字符串属性的错误 1.2.2(2020-3-8) 完善鉴权页面的配置,解决时长...
sping-boot-shiro-jwt-redis-refreshtoken,springboot整和shiro、jwt,自动刷新token
主要讲解了在structs怎样通过Token令牌解决表单重复提交的问题。附带了擦参考项目。
Sentinel 提供一个轻量级的开源控制台,它提供机器发现以及健康情况管理、监控(单机和集群),规则管理和推送的功能。是一款面向云原生微服务的高可用流控防护组件。
纯Struts2 struts-2.3.16.1版本下的demo,1、Struts2标签 2、Action 3、ActionSupport中validate 4、prepare 5、modeldriven 6、国际化 7、Token 8、拦截器
Spring Boot项目之用户登陆-利用用户令牌Token的方式实现