如何用令牌类去除第二次提交的数据
怎么用令牌类去除第二次提交的数据

比如卡的时候 按了2次回车
提交了2次 就直接在数据库插入了2条完全相同的记录
怎么当卡的时候 把第二次提交的数据过滤掉
这个是我的令牌类写法
怎么写的一个思路求指导
------解决思路----------------------
在表单里设置一个隐藏字段保存token,浏览器请求页面的时候也同时把token值存在服务器中!当表单提交时查看服务器中是否存在此token值,如果存在执行操作并且删除服务器中token,如果不存在则是重复提交拒绝操作执行!
楼主用Model存Token,我只知道的就是简单的存在Session域!不知道Model作用域范围!
------解决思路----------------------
为啥不在前端限制重复点击
------解决思路----------------------
分布式系统可以考虑存入memcached。
------解决思路----------------------
编写自定义标签的类:
public class TokenTag extends TagSupport {
/**
* 凡序列化
*/
private static final long serialVersionUID = -1815168785364991254L;
@Override
public int doStartTag() throws JspException {
// TODO Auto-generated method stub
/***
* 拦截器的两种原理
* 1:服务端生成一个唯一标示符 UUID并保持到session中 ,同时吧这个UUID也带到客户端页面,然后页面提交,
* 判断客户端页面中UUID和服务器端的UUID进行匹配,匹配成功表明是非重复提交,否则视为重复提交
*
* 2:服务端生成一个唯一标示符 UUID(不存入session,只是用于客户端的存储) ,把这个UUID也带到客户端页面,然后页面提交,
* 如果服务器的token为null,把客户端页面中UUID存入session中的token中,并允许提交,
* 在整个流程结束后,把该token值从session移除(在SpringMVCInterceptor->afterCompletion方法中移除)
*/
/*** 第一种原理实现方式 **/
//得到uuid
String uuid=UUID.randomUUID().toString().replace("-","");
Map<String,String> tokenMap = (Map<String, String>) pageContext.getSession().getAttribute("tokenMap") ;
if(tokenMap == null
------解决思路----------------------
tokenMap.size() == 0 ){
tokenMap = new HashMap<String,String>();
}
tokenMap.put(uuid, uuid);
//得到session,pageContext 是域对象同样也是工具类
pageContext.getSession().setAttribute("tokenMap",tokenMap);
String html="<input type='hidden' id='tokenName' name='tokenName' readonly='readonly' value="+uuid+">";
try {
pageContext.getOut().print(html);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return EVAL_BODY_INCLUDE;
/*** 第二种原理实现方式
//得到uuid
String uuid=UUID.randomUUID().toString().replace("-","");
String html="<input type='hidden' name='tokenName' readonly='readonly' value="+uuid+">";
try {
pageContext.getOut().print(html);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return EVAL_BODY_INCLUDE;
**/
}
}
请求拦截器,对所有的请求进行拦截 (我这里用的HandlerInterceptor,具体配置,可以到网上查看下)
/****
* spring Interceptor 请求拦截器,对所有的请求进行拦截
* 两种实现方式:
* 1:实现WebRequestInterceptor 接口
* 2:实现HandlerInterceptor 接口
* @author jinp
* 20140702
*/
public class SpringMVCInterceptor implements HandlerInterceptor {
/**
* preHandle方法是进行处理器拦截用的,顾名思义,该方法将在Controller处理之前进行调用,
* SpringMVC中的Interceptor拦截器是链式的,可以同时存在
* 多个Interceptor,然后SpringMVC会根据声明的前后顺序一个接一个的执行,而且所有的Interceptor中的preHandle方法都会在
* Controller方法调用之前调用。SpringMVC的这种Interceptor链式结构也是可以进行中断的,这种中断方式是令preHandle的返
* 回值为false,当preHandle的返回值为false的时候整个请求就结束了。
*/
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
/***
* 拦截器的两种原理
* 1:服务端生成一个唯一标示符 UUID并保持到session中 ,同时吧这个UUID也带到客户端页面,然后页面提交,
* 判断客户端页面中UUID和服务器端的UUID进行匹配,匹配成功表明是非重复提交,否则视为重复提交
*
* 2:服务端生成一个唯一标示符 UUID(不存入session,只是用于客户端的存储) ,把这个UUID也带到客户端页面,然后页面提交,
* 如果服务器的token为null,把客户端页面中UUID存入session中的token中,并允许提交,
* 在整个流程结束后,把该token值从session移除(在SpringMVCInterceptor->afterCompletion方法中移除)
*/
/*** 第一种原理实现方式 **/
// 防止重复提交 获得输入框中的值 token
String tokenName = request.getParameter("tokenName");
// 获得session中的值 把token值存入map中,防止打开多个修改页面的时候,无法保存提交
// String sessionToken = (String) request.getSession().getAttribute("token");
Map<String,String> tokenMap = (Map<String, String>) request.getSession().getAttribute("tokenMap");
// 判断当前请求 是form 提交还是 ajax 提交
// 如果requestType能拿到值,
// 并且值为XMLHttpRequest,表示客户端的请求为异步请求,那自然是ajax请求了,反之如果为null,则是普通的请求
String requestType = request.getHeader("X-Requested-With");
// synchronized 锁定 session
if(tokenName != null){
synchronized(request.getSession()) {
if (tokenMap.containsKey(tokenName)) {
// 为了保证session中的值不重复,所以移除
// request.getSession().removeAttribute("token");
tokenMap.remove(tokenName);
} else {
// 流程中断的话需要我们进行响应的处理
// 判断当前的请求方式 ,根据请求方式不同,处理也不同
response.setContentType("text/html;charset=UTF-8");
if("XMLHttpRequest".equals(requestType)){ // ajax请求直接返回提示结果
response.getWriter().print("对不起不能重复提交") ;
}else{ // form请求返回提示结果,并可以跳转到上一页面
response.getWriter().print("对不起不能重复提交, 点击 <input type='button' name='Submit' onclick='javascript:history.back(-1);' value='返回上一页'>");
}
return false;
}
}
}
return true;
/*** 第二种原理实现方式
// request.getHeader(arg0)
// 3秒钟后 调整到指定的页面
// response.setHeader("refresh", "3;URL=www.baidu.com");
System.out.println("url=====" + request.getRequestURI());
// 判断当前请求 是form 提交还是 ajax 提交
// 如果requestType能拿到值,并且值为XMLHttpRequest,表示客户端的请求为异步请求,那自然是ajax请求了,反之如果为null,则是普通的请求
String requestType = request.getHeader("X-Requested-With");
// 防止重复提交 获得输入框中的值 token
String tokenName = request.getParameter("tokenName");
if(request.getSession().getAttribute("tokenID") == null){
System.out.println("preHandle---------true---------");
request.getSession().setAttribute("tokenID", tokenName);
return true;
}else{
// 流程中断的话需要我们进行响应的处理
// 判断当前的请求方式 ,根据请求方式不同,处理也不同
response.setContentType("text/html;charset=UTF-8");
if("XMLHttpRequest".equals(requestType)){ // ajax请求直接返回提示结果
response.getWriter().print("对不起不能重复提交") ;
}else{ // form请求返回提示结果,并可以跳转到上一页面
response.getWriter().print("对不起不能重复提交, 点击 <input type='button' name='Submit' onclick='javascript:history.back(-1);' value='返回上一页'>");
}
System.out.println("preHandle---------false---------");
return false;
}
**/
}
/**
* 这个方法只会在当前这个Interceptor的preHandle方法返回值为true的时候才会执行。postHandle是进行处理器拦截用的,它的执行时间是在处理器进行处理之
* 后,也就是在Controller的方法调用之后执行,但是它会在DispatcherServlet进行视图的渲染之前执行,也就是说在这个方法中你可以对ModelAndView进行操
* 作。这个方法的链式结构跟正常访问的方向是相反的,也就是说先声明的Interceptor拦截器该方法反而会后调用,这跟Struts2里面的拦截器的执行过程有点像,
* 只是Struts2里面的intercept方法中要手动的调用ActionInvocation的invoke方法,Struts2中调用ActionInvocation的invoke方法就是调用下一个Interceptor
* 或者是调用action,然后要在Interceptor之前调用的内容都写在调用invoke之前,要在Interceptor之后调用的内容都写在调用invoke方法之后。
*/
@Override
public void postHandle(HttpServletRequest request,
HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
// TODO Auto-generated method stub
// System.out.println("postHandle------------------");
}
/**
* 该方法也是需要当前对应的Interceptor的preHandle方法的返回值为true时才会执行。该方法将在整个请求完成之后,也就是DispatcherServlet渲染了视图执行,
* 这个方法的主要作用是用于清理资源的,当然这个方法也只能在当前这个Interceptor的preHandle方法的返回值为true时才会执行。
*/
@Override
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response, Object handler, Exception ex) throws Exception {
// TODO Auto-generated method stub
/*** 第二种原理实现方式 **/
// request.getSession().removeAttribute("tokenID");
// System.out.println("afterCompletion------------------tokenID=" + request.getSession().getAttribute("tokenID"));
}
比如卡的时候 按了2次回车
提交了2次 就直接在数据库插入了2条完全相同的记录
怎么当卡的时候 把第二次提交的数据过滤掉
这个是我的令牌类写法
package com.wenda.cloudlib.common.util;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.SessionAttributes;
import com.wenda.cloudlib.constant.Constant;
/**
* <p>
* Description: 令牌类
* </p>
* Copyright: Copyright (c) 2012 Company: wenda
*
* @Author sfy
* @Version 1.0 2014-07-17
*/
@SessionAttributes(value = { Constant.TOKEN_KEY })
public class Token {
/**
* 保存令牌值
* @param model
*/
public static void saveToken(Model model) {
if (!model.containsAttribute(Constant.TOKEN_KEY)) {
String tokenValue = UUIDGenerator.getUUID();
model.addAttribute(Constant.TOKEN_KEY, tokenValue);
}
}
/**
* 清除令牌值
* @param model
*/
public static void clearToken(Model model) {
if (model.containsAttribute(Constant.TOKEN_KEY)) {
model.asMap().remove(Constant.TOKEN_KEY);
}
}
/**
* 验证令牌值是否为空
* @param model
* @return
*/
public static boolean isTokenEmpty(Model model) {
return model.containsAttribute(Constant.TOKEN_KEY);
}
}
怎么写的一个思路求指导
------解决思路----------------------
在表单里设置一个隐藏字段保存token,浏览器请求页面的时候也同时把token值存在服务器中!当表单提交时查看服务器中是否存在此token值,如果存在执行操作并且删除服务器中token,如果不存在则是重复提交拒绝操作执行!
楼主用Model存Token,我只知道的就是简单的存在Session域!不知道Model作用域范围!
------解决思路----------------------
为啥不在前端限制重复点击
------解决思路----------------------
分布式系统可以考虑存入memcached。
------解决思路----------------------
编写自定义标签的类:
public class TokenTag extends TagSupport {
/**
* 凡序列化
*/
private static final long serialVersionUID = -1815168785364991254L;
@Override
public int doStartTag() throws JspException {
// TODO Auto-generated method stub
/***
* 拦截器的两种原理
* 1:服务端生成一个唯一标示符 UUID并保持到session中 ,同时吧这个UUID也带到客户端页面,然后页面提交,
* 判断客户端页面中UUID和服务器端的UUID进行匹配,匹配成功表明是非重复提交,否则视为重复提交
*
* 2:服务端生成一个唯一标示符 UUID(不存入session,只是用于客户端的存储) ,把这个UUID也带到客户端页面,然后页面提交,
* 如果服务器的token为null,把客户端页面中UUID存入session中的token中,并允许提交,
* 在整个流程结束后,把该token值从session移除(在SpringMVCInterceptor->afterCompletion方法中移除)
*/
/*** 第一种原理实现方式 **/
//得到uuid
String uuid=UUID.randomUUID().toString().replace("-","");
Map<String,String> tokenMap = (Map<String, String>) pageContext.getSession().getAttribute("tokenMap") ;
if(tokenMap == null
------解决思路----------------------
tokenMap.size() == 0 ){
tokenMap = new HashMap<String,String>();
}
tokenMap.put(uuid, uuid);
//得到session,pageContext 是域对象同样也是工具类
pageContext.getSession().setAttribute("tokenMap",tokenMap);
String html="<input type='hidden' id='tokenName' name='tokenName' readonly='readonly' value="+uuid+">";
try {
pageContext.getOut().print(html);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return EVAL_BODY_INCLUDE;
/*** 第二种原理实现方式
//得到uuid
String uuid=UUID.randomUUID().toString().replace("-","");
String html="<input type='hidden' name='tokenName' readonly='readonly' value="+uuid+">";
try {
pageContext.getOut().print(html);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return EVAL_BODY_INCLUDE;
**/
}
}
请求拦截器,对所有的请求进行拦截 (我这里用的HandlerInterceptor,具体配置,可以到网上查看下)
/****
* spring Interceptor 请求拦截器,对所有的请求进行拦截
* 两种实现方式:
* 1:实现WebRequestInterceptor 接口
* 2:实现HandlerInterceptor 接口
* @author jinp
* 20140702
*/
public class SpringMVCInterceptor implements HandlerInterceptor {
/**
* preHandle方法是进行处理器拦截用的,顾名思义,该方法将在Controller处理之前进行调用,
* SpringMVC中的Interceptor拦截器是链式的,可以同时存在
* 多个Interceptor,然后SpringMVC会根据声明的前后顺序一个接一个的执行,而且所有的Interceptor中的preHandle方法都会在
* Controller方法调用之前调用。SpringMVC的这种Interceptor链式结构也是可以进行中断的,这种中断方式是令preHandle的返
* 回值为false,当preHandle的返回值为false的时候整个请求就结束了。
*/
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
/***
* 拦截器的两种原理
* 1:服务端生成一个唯一标示符 UUID并保持到session中 ,同时吧这个UUID也带到客户端页面,然后页面提交,
* 判断客户端页面中UUID和服务器端的UUID进行匹配,匹配成功表明是非重复提交,否则视为重复提交
*
* 2:服务端生成一个唯一标示符 UUID(不存入session,只是用于客户端的存储) ,把这个UUID也带到客户端页面,然后页面提交,
* 如果服务器的token为null,把客户端页面中UUID存入session中的token中,并允许提交,
* 在整个流程结束后,把该token值从session移除(在SpringMVCInterceptor->afterCompletion方法中移除)
*/
/*** 第一种原理实现方式 **/
// 防止重复提交 获得输入框中的值 token
String tokenName = request.getParameter("tokenName");
// 获得session中的值 把token值存入map中,防止打开多个修改页面的时候,无法保存提交
// String sessionToken = (String) request.getSession().getAttribute("token");
Map<String,String> tokenMap = (Map<String, String>) request.getSession().getAttribute("tokenMap");
// 判断当前请求 是form 提交还是 ajax 提交
// 如果requestType能拿到值,
// 并且值为XMLHttpRequest,表示客户端的请求为异步请求,那自然是ajax请求了,反之如果为null,则是普通的请求
String requestType = request.getHeader("X-Requested-With");
// synchronized 锁定 session
if(tokenName != null){
synchronized(request.getSession()) {
if (tokenMap.containsKey(tokenName)) {
// 为了保证session中的值不重复,所以移除
// request.getSession().removeAttribute("token");
tokenMap.remove(tokenName);
} else {
// 流程中断的话需要我们进行响应的处理
// 判断当前的请求方式 ,根据请求方式不同,处理也不同
response.setContentType("text/html;charset=UTF-8");
if("XMLHttpRequest".equals(requestType)){ // ajax请求直接返回提示结果
response.getWriter().print("对不起不能重复提交") ;
}else{ // form请求返回提示结果,并可以跳转到上一页面
response.getWriter().print("对不起不能重复提交, 点击 <input type='button' name='Submit' onclick='javascript:history.back(-1);' value='返回上一页'>");
}
return false;
}
}
}
return true;
/*** 第二种原理实现方式
// request.getHeader(arg0)
// 3秒钟后 调整到指定的页面
// response.setHeader("refresh", "3;URL=www.baidu.com");
System.out.println("url=====" + request.getRequestURI());
// 判断当前请求 是form 提交还是 ajax 提交
// 如果requestType能拿到值,并且值为XMLHttpRequest,表示客户端的请求为异步请求,那自然是ajax请求了,反之如果为null,则是普通的请求
String requestType = request.getHeader("X-Requested-With");
// 防止重复提交 获得输入框中的值 token
String tokenName = request.getParameter("tokenName");
if(request.getSession().getAttribute("tokenID") == null){
System.out.println("preHandle---------true---------");
request.getSession().setAttribute("tokenID", tokenName);
return true;
}else{
// 流程中断的话需要我们进行响应的处理
// 判断当前的请求方式 ,根据请求方式不同,处理也不同
response.setContentType("text/html;charset=UTF-8");
if("XMLHttpRequest".equals(requestType)){ // ajax请求直接返回提示结果
response.getWriter().print("对不起不能重复提交") ;
}else{ // form请求返回提示结果,并可以跳转到上一页面
response.getWriter().print("对不起不能重复提交, 点击 <input type='button' name='Submit' onclick='javascript:history.back(-1);' value='返回上一页'>");
}
System.out.println("preHandle---------false---------");
return false;
}
**/
}
/**
* 这个方法只会在当前这个Interceptor的preHandle方法返回值为true的时候才会执行。postHandle是进行处理器拦截用的,它的执行时间是在处理器进行处理之
* 后,也就是在Controller的方法调用之后执行,但是它会在DispatcherServlet进行视图的渲染之前执行,也就是说在这个方法中你可以对ModelAndView进行操
* 作。这个方法的链式结构跟正常访问的方向是相反的,也就是说先声明的Interceptor拦截器该方法反而会后调用,这跟Struts2里面的拦截器的执行过程有点像,
* 只是Struts2里面的intercept方法中要手动的调用ActionInvocation的invoke方法,Struts2中调用ActionInvocation的invoke方法就是调用下一个Interceptor
* 或者是调用action,然后要在Interceptor之前调用的内容都写在调用invoke之前,要在Interceptor之后调用的内容都写在调用invoke方法之后。
*/
@Override
public void postHandle(HttpServletRequest request,
HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
// TODO Auto-generated method stub
// System.out.println("postHandle------------------");
}
/**
* 该方法也是需要当前对应的Interceptor的preHandle方法的返回值为true时才会执行。该方法将在整个请求完成之后,也就是DispatcherServlet渲染了视图执行,
* 这个方法的主要作用是用于清理资源的,当然这个方法也只能在当前这个Interceptor的preHandle方法的返回值为true时才会执行。
*/
@Override
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response, Object handler, Exception ex) throws Exception {
// TODO Auto-generated method stub
/*** 第二种原理实现方式 **/
// request.getSession().removeAttribute("tokenID");
// System.out.println("afterCompletion------------------tokenID=" + request.getSession().getAttribute("tokenID"));
}