防止刷新或后退页面重复提交表单
2017-11-06 17:56
701 查看
刷新或后退页面会引起重复提交表单,为了避免这个头疼的问题发生,有几种方法可以实现。网上已经有很多实现的方式的思路,比如提交后禁用按钮、重定向和令牌,但前两种方式有时候不起作用或者说没有“安全感”,我觉得最好的实现方式应该是通过生成令牌(随机字符串)的方式由后端控制。
简单说下思路:
1.在呈现表单页面的时候,需要一个隐藏字段input,该值是后端随机生成的一个字符串(令牌)。该令牌存放在Session中。
2.页面提交时,同将这个令牌提交,后端接收该令牌,然后从Session中获取令牌,(然后立即移除Session中的该令牌属性,这样移除后可以保证一个提交请求只能对应一个令牌),将二者比较,不存在或不一样则判断为重复提交,此时可将页面定向到一个错误页面。
(下面使用是Java的MVC。)
1.跳转到表单页面
2.表单页面
3.订单提交
4.重复提交的错误页面 request_timeout.html
5.生成随机字符串的工具类RandomUtil.java
PS:表单提交后,处理请求并将SESSION_ORDER_TOKEN从Session中移除掉了,所以之后无论是刷新还是后退页面再次提交,由于Session中已经没有令牌了,所以会定向到错误提示页面!
《道德经》第三十三章:
知人者智,自知者明。胜人者有力,自胜者强。知足者富,强行者有志,不失其所者久,死而不亡者寿。
译文:能了解、认识别人叫做智慧,能认识、了解自己才算聪明。能战胜别人是有力的,能克制自己的弱点才算刚强。知道满足的人才是富有人。坚持力行、努力不懈的就是有志。不离失本分的人就能长久不衰,身虽死而“道”仍存的,才算真正的长寿。
简单说下思路:
1.在呈现表单页面的时候,需要一个隐藏字段input,该值是后端随机生成的一个字符串(令牌)。该令牌存放在Session中。
2.页面提交时,同将这个令牌提交,后端接收该令牌,然后从Session中获取令牌,(然后立即移除Session中的该令牌属性,这样移除后可以保证一个提交请求只能对应一个令牌),将二者比较,不存在或不一样则判断为重复提交,此时可将页面定向到一个错误页面。
(下面使用是Java的MVC。)
1.跳转到表单页面
public static final String SESSION_ORDER_TOKEN="SESSION_ORDER_TOKEN";//订单提交令牌,防止重复提交 @Autowired HttpSession session; //表单 @RequestMapping(value = "/orderForm.do",method = RequestMethod.POST) public String orderForm( ModelMap model) { session.setAttribute(SESSION_ORDER_TOKEN, RandomUtil.generateString(16));//设置一个令牌,防止重复提交 return "views/weixin/order_form"; }
2.表单页面
<form action="weixin/orderPost.do" method="post"> <input type="hidden" name="token" value="${Session.SESSION_ORDER_TOKEN!''}" > <input ...> <input ...> </form>
3.订单提交
//提交订单 @RequestMapping(value = "orderPost.do",method = RequestMethod.POST) public String orderPost(String user_id,String token, ModelMap model) { Object obj = session.getAttribute(SESSION_ORDER_TOKEN);//获得令牌 if(obj==null) return "views/weixin/request_timeout"; //移除令牌 无论成功还是失败 session.removeAttribute(SESSION_ORDER_TOKEN); String mToken = (String) obj; if(token==null||!mToken.equals(token)) return "views/weixin/request_timeout"; if(user_id==null||!user_id.equals(loginUser.getCustomerId()+"")){ return "redirect:/weixin/index.do";//如果用户标识不一致,则该笔订单无效,重定向到首页 } try { //业务逻辑 ... } catch (Exception e) { e.printStackTrace(); return "views/weixin/order_post_failure"; } return "views/weixin/order_post_succeed"; }
4.重复提交的错误页面 request_timeout.html
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Error</title> <meta name="viewport" content="width=device-width, initial-scale=1,maximum-scale=1, user-scalable=no"> <meta name="apple-mobile-web-app-capable" content="yes"> <meta name="apple-mobile-web-app-status-bar-style" content="black"> <link rel="stylesheet" href="${request.contextPath}/resources/mui/css/mui.min.css"> </head> <body> <div style="text-align: center;padding: 3px;"> <p style="font-size:18px;font-family: '微软雅黑';"> <h4>请求过时或请求页面已经失效! </p> <p style=" margin-bottom:20px;">您可以 <a href="${request.contextPath}/weixin/index.do">返回首页</a> 或去 <a href="${request.contextPath}/weixin/orderMgr.do">用户中心</a></p> </div> </body> </html>
5.生成随机字符串的工具类RandomUtil.java
package sy.util; import java.util.Random; public class RandomUtil { public static final String ALLCHAR = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; public static final String LETTERCHAR = "abcdefghijkllmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; public static final String NUMBERCHAR = "0123456789"; /** * 返回一个定长的随机字符串(只包含大小写字母、数字) * * @param length * 随机字符串长度 * @return 随机字符串 */ public static String generateString(int length) { StringBuffer sb = new StringBuffer(); Random random = new Random(); for (int i = 0; i < length; i++) { sb.append(ALLCHAR.charAt(random.nextInt(ALLCHAR.length()))); } return sb.toString(); } /** * 返回一个定长的随机纯字母字符串(只包含大小写字母) * * @param length * 随机字符串长度 * @return 随机字符串 */ public static String generateMixString(int length) { StringBuffer sb = new StringBuffer(); Random random = new Random(); for (int i = 0; i < length; i++) { sb.append(LETTERCHAR.charAt(random.nextInt(LETTERCHAR.length()))); } return sb.toString(); } /** * 返回一个定长的随机纯大写字母字符串(只包含大小写字母) * * @param length * 随机字符串长度 * @return 随机字符串 */ public static String generateLowerString(int length) { return generateMixString(length).toLowerCase(); } /** * 返回一个定长的随机纯小写字母字符串(只包含大小写字母) * * @param length * 随机字符串长度 * @return 随机字符串 */ public static String generateUpperString(int length) { return generateMixString(length).toUpperCase(); } /** * 生成一个定长的纯0字符串 * * @param length * 字符串长度 * @return 纯0字符串 */ public static String generateZeroString(int length) { StringBuffer sb = new StringBuffer(); for (int i = 0; i < length; i++) { sb.append('0'); } return sb.toString(); } /** * 根据数字生成一个定长的字符串,长度不够前面补0 * * @param num * 数字 * @param fixdlenth * 字符串长度 * @return 定长的字符串 */ public static String toFixdLengthString(long num, int fixdlenth) { StringBuffer sb = new StringBuffer(); String strNum = String.valueOf(num); if (fixdlenth - strNum.length() >= 0) { sb.append(generateZeroString(fixdlenth - strNum.length())); } else { throw new RuntimeException("将数字" + num + "转化为长度为" + fixdlenth + "的字符串发生异常!"); } sb.append(strNum); return sb.toString(); } /** * 每次生成的len位数都不相同 * * @param param * @return 定长的数字 */ public static int getNotSimple(int[] param, int len) { Random rand = new Random(); for (int i = param.length; i > 1; i--) { int index = rand.nextInt(i); int tmp = param[index]; param[index] = param[i - 1]; param[i - 1] = tmp; } int result = 0; for (int i = 0; i < len; i++) { result = result * 10 + param[i]; } return result; } public static void main(String[] args) { System.out.println("返回一个定长的随机字符串(只包含大小写字母、数字):" + generateString(10)); System.out .println("返回一个定长的随机纯字母字符串(只包含大小写字母):" + generateMixString(10)); System.out.println("返回一个定长的随机纯大写字母字符串(只包含大小写字母):" + generateLowerString(10)); System.out.println("返回一个定长的随机纯小写字母字符串(只包含大小写字母):" + generateUpperString(10)); System.out.println("生成一个定长的纯0字符串:" + generateZeroString(10)); System.out.println("根据数字生成一个定长的字符串,长度不够前面补0:" + toFixdLengthString(123, 10)); int[] in = { 1, 2, 3, 4, 5, 6, 7 }; System.out.println("每次生成的len位数都不相同:" + getNotSimple(in, 3)); } }
PS:表单提交后,处理请求并将SESSION_ORDER_TOKEN从Session中移除掉了,所以之后无论是刷新还是后退页面再次提交,由于Session中已经没有令牌了,所以会定向到错误提示页面!
《道德经》第三十三章:
知人者智,自知者明。胜人者有力,自胜者强。知足者富,强行者有志,不失其所者久,死而不亡者寿。
译文:能了解、认识别人叫做智慧,能认识、了解自己才算聪明。能战胜别人是有力的,能克制自己的弱点才算刚强。知道满足的人才是富有人。坚持力行、努力不懈的就是有志。不离失本分的人就能长久不衰,身虽死而“道”仍存的,才算真正的长寿。
相关文章推荐
- asp.net防止页面刷新或后退引起重复提交
- ASP.NET中防止页面刷新造成表单重复提交执行两次操作
- django防止页面重新载入,刷新重复提交表单解决方案
- 防止刷新页面造成表单重复提交
- asp.net防止页面刷新或后退引起重复提交
- 防止页面重复刷新、重复提交、后退的解决方案
- ASP.NET中防止刷新页面造成表单重复提交
- asp.net 防止页面刷新或后退引起重复提交
- ASP.NET中防止刷新页面造成表单重复提交
- 防止页面刷新时表单的重复提交的最简单方法(C#)
- 【学习笔记】Web开发中防止页面刷新后表单重复提交,表单Token设置示例
- 防止页面重复提交、刷新、后退与数据库中的重复记录
- asp.net防止页面刷新或后退引起重复提交
- ASP.NET中防止页面刷新造成表单重复提交执行两次操作
- <转>如何防止页面刷新,后退导致的重复提交
- jsp中防止刷新后退等操作造成表单重复提交(纯jsp或者struts)
- ASP.NET中防止刷新页面造成表单重复提交
- .net 防止页面刷新重复提交(表单提交后保留上一次数据缓存,刷新提交)
- WebForm中如何防止页面刷新,后退导致的重复提交
- ASP.NET中防止刷新页面造成表单重复提交