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

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>


  
    提交表单
    
    
    
  
  
    
用户名:
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;

/*
 * 服务器端防表单重复提交思路:
 *	要在服务器端防表单重复提交,表单应该有一个程序输出浏览器,
 *	程序在把表单打给浏览器的时候,在每一个表单中都带上一个唯一的表单号,
 *	并且服务器会把这个唯一的表单号在服务器端也存储一份,
 *	浏览器提交表单的时候会带着表单号过来,服务器检测带过来的表单号在服务器端有没有,
 *	如果有的话代表这个表单没有提交过,就让这个表单号提交,
 *	然后马上把这个已经提交了的表单号在服务器端删除,下次再提交的时候带表单号过来,
 *	服务器端没有表单号了,服务器端就不让表单提交了。
 */
//产生表单的servlet
public 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"%>


  
    由程序输出,带有随机表单号的表单
  
  
    
用户名:

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表达式后面(${}后面,${里面可以有空格})千万不要多空格!否则数据后面也会多空格!


你可能感兴趣的:(自己用)