防止刷新或后退页面重复提交表单

刷新或后退页面会引起重复提交表单,为了避免这个头疼的问题发生,有几种方法可以实现。网上已经有很多实现的方式的思路,比如提交后禁用按钮、重定向和令牌,但前两种方式有时候不起作用或者说没有“安全感”,我觉得最好的实现方式应该是通过生成令牌(随机字符串)的方式由后端控制。

简单说下思路:
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中已经没有令牌了,所以会定向到错误提示页面!

《道德经》第三十三章:
知人者智,自知者明。胜人者有力,自胜者强。知足者富,强行者有志,不失其所者久,死而不亡者寿。
译文:能了解、认识别人叫做智慧,能认识、了解自己才算聪明。能战胜别人是有力的,能克制自己的弱点才算刚强。知道满足的人才是富有人。坚持力行、努力不懈的就是有志。不离失本分的人就能长久不衰,身虽死而“道”仍存的,才算真正的长寿。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值