java防止表单二次提交_防止表单重复提交

在Web开发中表单的重复提交是很严重的问题,重复提交成功会产生垃圾数据消耗不必要的资源,更严重的是如果遇到恶意刷库的情况垃圾数据更是数不胜数。在正常使用过程中产生重复提交的情况也有多重情况:鼠标连击、回退提交、刷新提交、网络延迟用户重复提交等。

防止重复提交的方法分两大类就是客户端、服务端(这是废话了)。客户端主要是用js对按钮的限制,一次点击后屏蔽按钮或者是直接跳转等待页面,服务端思路为客户端加token进行验证。客户端就不做详细介绍,主要介绍服务端的控制。

1、客户端存储

就是在客户端不同的地方存储两个token,在服务端进行校验。在Form表单中存储一个token利用隐藏域,在Cookie中存储一个(也可以都放到form表单中两个不同的隐藏域)。档form表单提交的时候,对这两个token进行验证,相同则允许提交否则阻止提交。

优点:

不占用服务器资源

实施起来简单,易上手

缺点:

容易伪造(防君子不防小人)

占用网络资源(或许不是那么明显)

详细介绍一下客户端分布存储在Form表单中和Cookie中的情况。

客户端的实现如下:

f27b204cd3fe403bc2d2cc994c71c5ae.gif

db04d6cce622d35aa63b54a26fd553d7.gif

packagecn.simple.token;importjavax.servlet.http.Cookie;importjavax.servlet.http.HttpServletRequest;importjavax.servlet.http.HttpServletResponse;importorg.apache.commons.lang.StringUtils;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.stereotype.Service;/*** 双客户端验证

*@authorldm

* @Date 2016年6月16日*/@Service("clientTokenProcesser")public class ClientTokenProcesser extendsTokenProcesser {

@Autowired

HttpServletResponse response;

@Overridepublic booleanvalidToken(HttpServletRequest request) {

String formToken=request.getParameter(getTokenField()).toString();

System.out.println("formToken:"+formToken);if(StringUtils.isEmpty(formToken))

{

printException("表单中没有token");return false;

}

Cookie[] cookies=request.getCookies();if(cookies==null)

{

printException("cookie 中没有token");

}for(Cookie cookie : cookies) {if(cookie.getName().equals(getTokenKey(request)))

{

String cookieValue=cookie.getValue();

System.out.println("cookieToken:"+cookieValue);if(cookieValue.equals(formToken))

{return true;

}

}

}return false;

}private voidprintException(String msg) {

Exception e= newRuntimeException(msg);

e.printStackTrace();

}

@OverridepublicString getTokenKey(HttpServletRequest request) {

String cookieKey= getTokenField() + "_cookie";returncookieKey;

}

@Overridepublic voidsaveToken(HttpServletRequest request) {

String token=MakeToken.getInstance().getToken();

request.setAttribute(getTokenField(), token);if (response == null) {throw new RuntimeException("HttpServletResponse is null");

}

Cookie cookie= newCookie(getTokenKey(request), token);

response.addCookie(cookie);

}

@OverridepublicString getClientToken(HttpServletRequest request) {

Object token=request.getParameter(getTokenField());if (token == null) {return null;

}else{returntoken.toString();

}

}

}

View Code

2、双向存储

客户端和服务端的token各自独立存储,客户端存储在Cookie或者Form的隐藏域(放在Form隐藏域中的时候,需要每个表单)中,服务端存储在Session(单机系统中可以使用)或者其他缓存系统(分布式系统可以使用)中。

优点:

安全性高(几乎是无法伪造的)

网络资源相对于前者有所减少

缺点:

整个系统实施起来较第一种方法的时候复杂度增加

详细介绍一下服务端存储在session中客户端存储在Cookie中

SessionTokenProcesser实现如下:

f27b204cd3fe403bc2d2cc994c71c5ae.gif

db04d6cce622d35aa63b54a26fd553d7.gif

packagecn.simple.token;importjavax.servlet.http.Cookie;importjavax.servlet.http.HttpServletRequest;importjavax.servlet.http.HttpServletResponse;importjavax.servlet.http.HttpSession;importorg.apache.commons.lang.StringUtils;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.stereotype.Service;/*** 服务端用Session存储

*

*@authorldm

* @Date 2016年6月16日*/@Service("sessionTokenProcesser")public class SessionTokenProcesser extendsTokenProcesser {

@Autowired

HttpServletResponse response;

@Overridepublic booleanvalidToken(HttpServletRequest request) {

String clientToken=getClientToken(request);if(StringUtils.isEmpty(clientToken)) {return false;

}

HttpSession session= request.getSession(false);if (session == null) {return false;

}

String tokenKey=getTokenKey(request);

Object tokenObj=session.getAttribute(tokenKey);if(tokenObj==null)

{

rethrow("服务端不存在当前token,请重新请求表单");

}

String serverToken=tokenObj.toString();

session.removeAttribute(tokenKey);

System.out.println("remove server token:" +serverToken);returnclientToken.equals(serverToken);

}

@OverridepublicString getTokenKey(HttpServletRequest request) {returngetTokenField();

}

@Overridepublic voidsaveToken(HttpServletRequest request) {

HttpSession session=request.getSession();

String tokenKey=getTokenKey(request);

Object tokenObj=session.getAttribute(tokenKey);

String token;if (tokenObj == null) {

token=MakeToken.getInstance().getToken();//服务端保存token

session.setAttribute(tokenKey, token);

}else{

token=tokenObj.toString();

}

System.out.println("current token:" +token);//写入cookie

Cookie cookie = newCookie(getTokenField(), token);

response.addCookie(cookie);

}private voidrethrow(String message) {

RuntimeException e= newRuntimeException(message);throwe;

}

@OverridepublicString getClientToken(HttpServletRequest request) {

Cookie[] cookies=request.getCookies();if (cookies == null) {

rethrow("没有读取到客户端的cookie");return null;

}for(Cookie cookie : cookies) {if(cookie.getName().equals(getTokenKey(request))) {

String cookieValue=cookie.getValue();returncookieValue;

}

}

rethrow("客户端cookie中没有存储token");return null;

}

}

View Code

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值