SessionToken防止表单重复提交

后端解决表单重复提交: 

       服务器端生成一个唯一的随机标识号,专业术语称为Token(令牌),同时在当前用户的Session域中保存这个Token。然后将Token发送到客户端的Form表单中,在Form表单中使用隐藏域来存储这个Token,表单提交的时候连同这个Token一起提交到服务器端,然后在服务器端判断客户端提交上来的Token与服务器端生成的Token是否一致,如果不一致,那就是重复提交了,此时服务器端就可以不处理重复提交的表单。如果相同则处理表单提交处理完清除当前用户的Session域中存储的标识号

在下列情况下,服务器程序将拒绝处理用户提交的表单请求:

  • 存储Session域中的Token(令牌)与表单提交的Token(令牌)不同。
  • 当前用户的Session中不存在Token(令牌)。
  • 用户提交的表单数据中没有Token(令牌)。

跳转表单,生成token,存储到session中:

package com.example.demo.learn.servlet.session.duplicatesubmit;

import com.example.demo.learn.Constants;
import com.example.demo.learn.servlet.session.utils.TokenUtils;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

@Controller
public class FormController {

    /**
     * 跳转到页面, 生成一个token
     *
     * @return 具体页面位置
     */
    @RequestMapping("toPage")
    public String toPage(HttpServletRequest request, Model model) {
        String token = TokenUtils.getToken();
        model.addAttribute(Constants.SESSION_TOKEN, token);
        request.getSession().setAttribute(Constants.SESSION_TOKEN, token);
        return "form";
    }
}

表单页面: 

<html xmlns:th="http://www.thymeleaf.org">
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<head>
    <title>表单防止重复提交</title>
    <script>
        var flag = false;
        function isSubmit() {
            if (!flag) {
                flag = true;
                return true;
            } else {
                return false;
            }
        }

        function doSubmit(){
            //获取表单提交按钮
            var btnSubmit = document.getElementById("submit");
            //将表单提交按钮设置为不可用,这样就可以避免用户再次点击提交按钮
            btnSubmit.disabled= "disabled";
            //返回true让表单可以正常提交
            return true;
        }
    </script>
</head>
<body>
    <!-- 前端提交解决
    <form action="/song/DuplicateSubmitResolve" method="post" onsubmit="doSubmit()">
        用户名:<input type="text" name="userName">
        <input type="submit" value="提交" id="submit">
    </form>
    -->

    <form action="/song/DuplicateSubmitResolve" method="post" onsubmit="doSubmit()">
        用户名:<input type="text" name="userName">
        <input type="text" name="sessionToken" th:value="${sessionToken}" >
        <input type="submit" value="提交" id="submit">
    </form>

</body>
</html>

 表单处理类:

package com.example.demo.learn.servlet.session.duplicatesubmit;

import com.example.demo.learn.Constants;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.io.PrintWriter;

/**
 * 解决重复提交问题
 * <p>
 * <p>
 * 表单重复提交的原因?
 * 1. 网路延迟
 * 2. 表单刷新重复提交
 * <p>
 * 表单重复提交解决办法:
 * 1. 前端解决:
 * 提交按钮只能触发一次
 * 提交按钮变灰
 * 2. 后端解决:
 * 使用token + session
 */

@WebServlet("/DuplicateSubmitResolve")
public class DuplicateSubmitResolve extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doPost(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String userName = req.getParameter("userName");

        resp.setCharacterEncoding("UTF-8");
        resp.setContentType("text/html;charset=UTF-8");
        PrintWriter out = resp.getWriter();

        if (!isSubmit(req)) {
            out.write("您已经提交了数据或者无效的token.....");
            return;
        }

        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("模拟插入数据库.... userName: " + userName);
        out.write("保存成功");
        // 保存之后删除sessionToken
        req.getSession().removeAttribute(Constants.SESSION_TOKEN);
    }

    /**
     * 校验是否提交
     *
     * @param req request
     * @return boolean
     */
    private boolean isSubmit(HttpServletRequest req) {
        String parameterToken = req.getParameter("sessionToken");
        // 获得token
        HttpSession session = req.getSession();
        String sessionToken = (String) session.getAttribute(Constants.SESSION_TOKEN);
        if (sessionToken == null || parameterToken == null) {
            return false;
        }
        // 防止伪造token
        if (!parameterToken.equals(sessionToken)) {
            return false;
        }
        return true;
    }
}
package com.example.demo.learn;

public class Constants {

    public static final String SESSION_TOKEN = "sessionToken";

}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值