javaweb之Session客户端防表单重复提交(js)和服务端Session防表单重复提交

来源:互联网 发布:如何避开公司网络监控 编辑:程序博客网 时间:2024/06/08 10:27
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"><html>  <head>    <title>提交表单</title>    <!-- 光有js是不能阻止表单重复提交的,比如别人直接拿你的源代码去掉js,修改地址后是可以提交的 ,js是防不死的,有很多漏洞,利用刷新后退等也可以提交多次-->    <!-- 客户端用js阻止表单重复提交 -->    <script type="text/javascript">    //方法一:只提交一次表单    //设置好一个是否已经提交的全局变量    var iscommitted = false;    function dosubmit() {    //如果还没有提交    if(!iscommitted) {    //将全局变量设置为true,表示已经提交    iscommitted = true;    //返回可以提交    return true;    } else {    //如果已经提交,返回不可以提交    return false;    }    }        /*    方法二:    //点击提交后设置按钮不可以再点击    function dosubmit() {    var input = document.getElementById("submit");    input.disabled = 'disabled'    return true;    }    */    </script>  </head>  <body>    <form action="/day07/servlet/DoFormServlet" method="post" onsubmit="return dosubmit()">    用户名:<input type="text" name="username"/>    <input id="submit" type="submit" value="登录"/>    </form>  </body></html>
package test.form;import java.io.IOException;import java.security.MessageDigest;import java.security.NoSuchAlgorithmException;import java.util.Random;import javax.servlet.ServletException;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import sun.misc.BASE64Encoder;/* * 服务器端防表单重复提交思路: *要在服务器端防表单重复提交,表单应该有一个程序输出浏览器, *程序在把表单打给浏览器的时候,在每一个表单中都带上一个唯一的表单号, *并且服务器会把这个唯一的表单号在服务器端也存储一份, *浏览器提交表单的时候会带着表单号过来,服务器检测带过来的表单号在服务器端有没有, *如果有的话代表这个表单没有提交过,就让这个表单号提交, *然后马上把这个已经提交了的表单号在服务器端删除,下次再提交的时候带表单号过来, *服务器端没有表单号了,服务器端就不让表单提交了。 *///产生表单的servletpublic class FormServlet extends HttpServlet {public void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {// 产生随机数(表单号)TokenProcessor tp = TokenProcessor.getInstance();String token = tp.generateToken();// servlet不适合输出表单,要转发到jsp页面,也要把表单号带过去,并且以后防止表单重复提交的时候还要用这个表单号,所以要存在session中,不能存在request中request.getSession().setAttribute("token", token);request.getRequestDispatcher("/form2.jsp").forward(request, response);}public void doPost(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {}}// 产生随机表单号的类class TokenProcessor { // Token令牌// 为了保证令牌发生器产生的令牌是唯一的,通常会把这个令牌发生器设计成单立的,因为一个对象创建的随机数重复的概率低private TokenProcessor() {}private static final TokenProcessor instance = new TokenProcessor();public static TokenProcessor getInstance() {return instance;}// 产生随机数的方法public String generateToken() {// 根据当前毫秒值和一个随机数产生随机数,但是随机数长度不一致String token = System.currentTimeMillis() + new Random().nextInt() + "";// 希望得到随机数长短一样,要拿到随机数的数据摘要(指纹),不管人有多大,指纹都是一样的,不管数据多大,数据指纹始终是128bit(128位)// 运用指纹算法,得到固定长度的随机数try {// 得到数据的摘要(指纹)的算法,用md5算法算出数据摘要MessageDigest md = MessageDigest.getInstance("md5");// 对接收的数据进行摘要运算,得到数据摘要,数据不管多大,返回的指纹只有128bit(16个字节)byte[] md5 = md.digest(token.getBytes());// 不能直接用这个字节数据构建字符串返回,这样的话,没有指定码表,肯定会是乱码// return new String(md5);// 要用base64编码产生字符串,任何数据经过base64编码返回的都是明文字符串,键盘上能看到的字符组成的字符串。把三个字节变成四个字节。// base64有一个自己定义的码表,它会查自己的码表BASE64Encoder encoder = new BASE64Encoder();// 返回base64编码后的字符串return encoder.encode(md5);} catch (NoSuchAlgorithmException e) {throw new RuntimeException(e);}}}

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"><html>  <head>    <title>由程序输出,带有随机表单号的表单</title>  </head>  <body>    <form action="/day07/servlet/DoFormServlet" method="post">    <!-- 表单号由隐藏域提交过去 -->    <!-- 千万注意!在EL表达式后面千万不要多空格!否则数据后面也会多空格! -->    <input type="hidden" name="token" value="${token}"/>    用户名:<input type="text" name="username"/><br />    <input id="submit" type="submit" value="提交"/>    </form>  </body></html>

package test.form;import java.io.IOException;import javax.servlet.ServletException;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;//处理表单提交servlet(防表单重复提交功能和struts做法一样)//防表单重复提交要js和服务端都阻止,这才能彻底的防止表单重复提交public class DoFormServlet extends HttpServlet {public void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {form2Test(request);}// 处理form2页面提交的请求(服务器端防表单重复提交)private void form2Test(HttpServletRequest request) {// 防止表单重复提交代码// 检查表单号是否有效boolean b = isTokenValid(request);// 如果是true,意味着表单号有效,可以提交,如果是false,表单号无效,阻止表单提交if (!b) {// 阻止提交其实可以什么都不干(没有响应),不过为了看阻止效果,输出一句话System.out.println("请不要重复提交");return;}// 如果程序没有返回,执行到这里代表表单号有效// 处理表单提交之前,表单号要置为无效(从服务器端把已经提交过的表单号删掉)request.getSession().removeAttribute("token");// 处理表提交,向数据库中注册用户System.out.println("向数据库中注册用户");}// 处理form页面提交的请求(js防表单重复提交)private void formTest(HttpServletRequest request) {String username = request.getParameter("username");// 模仿网络延迟效果try {Thread.sleep(1000 * 3);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("向数据库中注册用户~~~~");}// 检查表单号是否有效private boolean isTokenValid(HttpServletRequest request) {// 得到客户机带过来的表单号String client_token = request.getParameter("token");// 没有带表单号过来认为是重复提交if (client_token == null) {return false;}// 判断服务器中有没有客户机带过来的表单号// 拿到服务器中的表单号String server_token = (String) request.getSession().getAttribute("token");// 判断服务器中有没有表单号,如果服务器中没有表单号,服务器端就已经把表单号提交了、删除了,没有表单号就代表提交过了if (server_token == null) {return false;}// 判断服务器端的表单号和客户机带过来的表单号是否一致,不一致认为是重复提交if (!server_token.equals(client_token)) {return false;}// 如果上面的检查都通过了,就代表可以提交return true;}public void doPost(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {doGet(request, response);}}/* * 验证本程序的时候先在浏览器中提交一次(第一次提交是正常的,不是重复提交),然后刷新页面或者后退一次再提交,就会看到重复提交提示。 */// 千万注意!在EL表达式后面(${}后面,${里面可以有空格})千万不要多空格!否则数据后面也会多空格!


0 0
原创粉丝点击