javaWeb应用后端防止表单重复提交
正常我们防止一个页面的表单重复提交有2种途径
1:客户端控制(比如js判断,按钮置灰不可用等等这个大家自行网上查询)
2:服务器端针对api自己多业务逻辑判断
实际使用的场景中,我们大多是2者结合起来做,不能把所有的判断逻辑都扔到服务器端,这对服务器端也是一种压力,能再客户端做一层拦截的,尽量先在客户端做掉。
客户端分别是基于两种方式实现:redis和session,个人倾向于redis
----------------------------------------------------------------------分割线------------redis版----------------------------------------------------------
1:redis方式,直接贴代码
private static String FORMTOKE = "form_token";
@Autowired
@Qualifier("forObject")
private RedisTemplate<String, Object> redisTemplate;
/**
* 基于redis的放置表单重复提交的demo
* */
@RequestMapping("/testRedisform")
public String index(HttpServletRequest request) {
String formToken = UUID.randomUUID().toString();//创建令牌
System.out.println("在FormServlet中生成的formToken:" + formToken);
// 使用redis来存储这个 令牌
redisTemplate.opsForValue().set(FORMTOKE, formToken);
//由于我这里的项目不是前后端分离的, 这里仍然借助session 把值方法页面上。当然前后端分离的情况下,这个值,直接可以通过返回值的dto 返回给客户端,无须用到seesion,客户端拿到放到页面的隐藏域里保存起来
request.getSession().setAttribute("formToken", formToken);
return "testForm";
}
/**
* 第二步 对提交过来的表单进行验证
* */
@RequestMapping("/vaRedisSubmiit")
public String vaSubmiit(HttpServletRequest request) {
boolean b = isRepeatSubmit(request);//判断用户是否是重复提交
if(b==true){
System.out.println("请不要重复提交--跳转一个提示的页面提示不允许重复提交");
return "cfSumiit";
}
//------doSomeThing---处理允许提交的逻辑------
// 处理完毕之后,一定记得要在redis里移除这个key值,不然就发生重复提交了
redisTemplate.delete(FORMTOKE); //移除redis中的token
System.out.println("否则提示成功---处理用户提交请求!!--比如跳转到提交成功的页面");
return "formSucc";
}
/**
* 判断客户端提交上来的令牌和服务器端生成的令牌是否一致
* @param request
* @return
* true 用户重复提交了表单
* false 用户没有重复提交表单
*/
private boolean isRepeatSubmit(HttpServletRequest request) {
String client_token = request.getParameter("formToken");
//1、如果用户提交的表单数据中没有token,则用户是重复提交了表单
if(client_token==null){
return true;
}
//取出存储在redis中的token
String server_token = (String) redisTemplate.opsForValue().get(FORMTOKE);
//2、如果redis里的中不存在Token(令牌),则用户是重复提交了表单
if(server_token==null){
return true;
}
//3、存储在Session中的Token(令牌)与表单提交的Token(令牌)不同,则用户是重复提交了表单
if(!client_token.equals(server_token)){
return true;
}
return false;
}
// jsp的代码我就不贴了,类似下面的session,后面的我把代码直接放到github上,大家如果需要自行下载
----------------------------------------------------------------------分割线------------session版----------------------------------------------------------
2:seesion方式,直接贴代码
/**
* 第一步在跳转到保存的表单界面的时候 先生成toke
* 放到session里存储起来,用于后面的表单提交的时候,做比对,同时这个值要放到页面的隐藏域上
* 下次提交表单的时候把这个隐藏域里的值也必须一起提交过来
* 这个放到session里是否可以考虑用redis里扩展?因为在分布式的情况下,还要考虑分布式的seeson
* */
@RequestMapping("/testform")
public String index(HttpServletRequest request) {
String formToken = UUID.randomUUID().toString();//创建令牌
System.out.println("在FormServlet中生成的token:" + formToken);
request.getSession().setAttribute("formToken", formToken); //在服务器使用session保存token(令牌)
return "testForm";
}
/**
* 第二步 对提交过来的表单进行验证
* */
@RequestMapping("/vaSubmiit")
public String vaSubmiit(HttpServletRequest request) {
boolean b = isRepeatSubmit(request);//判断用户是否是重复提交
if(b==true){
System.out.println("请不要重复提交--跳转一个提示的页面提示不允许重复提交");
return "cfSumiit";
}
//------doSomeThing---处理允许提交的逻辑------
// 处理完毕之后,一定记得要在session里移除这个key值,不然就发生重复提交了,关于这里,尽量考虑用redis来替换
request.getSession().removeAttribute("token");//移除session中的token
System.out.println("否则提示成功---处理用户提交请求!!--比如跳转到提交成功的页面");
return "formSucc";
}
/**
* 判断客户端提交上来的令牌和服务器端生成的令牌是否一致
* @param request
* @return
* true 用户重复提交了表单
* false 用户没有重复提交表单
*/
private boolean isRepeatSubmit(HttpServletRequest request) {
String client_token = request.getParameter("formToken");
//1、如果用户提交的表单数据中没有token,则用户是重复提交了表单
if(client_token==null){
return true;
}
//取出存储在Session中的token
String server_token = (String) request.getSession().getAttribute("formToken");
//2、如果当前用户的Session中不存在Token(令牌),则用户是重复提交了表单
if(server_token==null){
return true;
}
//3、存储在Session中的Token(令牌)与表单提交的Token(令牌)不同,则用户是重复提交了表单
if(!client_token.equals(server_token)){
return true;
}
return false;
}
// 下面贴出对应的jsp页面:
<form action="http://localhost:8080/vaSubmiit" οnsubmit="return dosubmit()" method="post">
<%--使用EL表达式取出存储在session中的token,实际情况下,请用 hidden属性 隐藏--%>
<input type="text" name="formToken" value="${formToken}"/>
userName:<input type="text" name="username">
<input type="submit" value="提交" id="submit">
</form>
整个源码下载地址:https://github.com/gitlyb2080/webFormVaDemo
启动之后:先访问:http://localhost:8080/testRedisform ,得到表单的提交页面
然后,点击 submmit