思想:通过验证码的形式防止表单重复提交。
通过JavaEE实现:
思路:
1,写一个表单,比如是用户注册表单,在表单中定义一个img标签,将src指定为一个servlet,这个servlet将动态生成一个验证码图片,也就是每次请求这个页面的时候都会生成一个新的验证码。
2,Servlet生成验证码,在这个servlet生成验证码之后,要将这个验证码的值存放在session中,用户放置重复提交,用到的token机制(令牌机制)。Servlet生成验证码的代码如下:
public class VerifyCodeServlet extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //产生验证码。 //首先准备一些数据。 String data = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz"; //准备随机函数,从这些数据中随机取值, Random random = new Random(); int width = 60; int height = 30; //创建图像。 BufferedImage image = new BufferedImage(width,height,BufferedImage.TYPE_INT_RGB); //创建画板。 Graphics g = image.getGraphics(); //设置画笔颜色。 g.setColor(Color.BLACK); //填充矩形。 g.fillRect(0, 0, width, height); g.setColor(Color.WHITE); g.fillRect(1, 1, width-2, height-2); //设置字体。 g.setFont(new Font("宋体",Font.BOLD,20)); //创建一个StringBuilder来构建随机生成 的四位验证码 StringBuilder sb = new StringBuilder(); //用循环获取4为随机码。 for(int i=0;i<4;i++) { //设置随机颜色。 g.setColor(new Color(random.nextInt(255),random.nextInt(255),random.nextInt(255))); //获得随机字符。 int index = random.nextInt(data.length()); String str = data.substring(index, index+1); //添加到缓冲的sb中。 sb.append(str); //绘制到画板中。 g.drawString(str,width/6*(i+1),20); } //将数据缓存到session,以便于在检查的servlet中进行验证码的比对。 String bufferData = sb.toString(); request.getSession().setAttribute("sessionVerifyCodeData", bufferData); //添加干扰点,线 for(int i=0;i<5;i++) { g.setColor(new Color(random.nextInt(255),random.nextInt(255),random.nextInt(255))); g.drawLine(random.nextInt(width), random.nextInt(height), random.nextInt(width), random.nextInt(height)); } //将服务器内存中的数据发送到浏览器。 ImageIO.write(image,"jpg",response.getOutputStream()); } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } }
3,用户填好验证码表单,提交到servlet,servlet先获取到用户提交的验证码,再获取服务器的验证码。首先判断服务器获取的验证码是否为空,如果是null,则提示用户“重复提交”,让用户重新刷新页面,重新提交。如果服务器有数据,则先将服务器的session中的数据删除,保证用户重复提交的时候服务器的session中已经没有数据,删除之后再判断session中的数据是否与用户提交的数据一致,一致则注册成功,不一致则返回信息提示用户验证码错误。
//判断验证码---------------------------------------------------------------------- //获取用户填写的验证码 String verifyCode = request.getParameter("verifyCode"); //获取session中的验证码,如果二者一致,则填写正确。 String sessionVerifyCodeData = (String)request.getSession().getAttribute("sessionVerifyCodeData"); //首先判断服务器是否有session中的验证码数据。 if(sessionVerifyCodeData!=null) { //有数据。将session中缓存的数据移除,保证验证码的一次性, request.getSession().removeAttribute("sessionVerifyCodeData"); if(!sessionVerifyCodeData.equalsIgnoreCase(verifyCode)) { //如果不匹配 request.setAttribute("message", "验证码有误,请重新填写"); request.getRequestDispatcher("/client/register.jsp").forward(request, response); return; } } else { //重复提交 request.setAttribute("message", "不要重复提交"); request.getRequestDispatcher("/client/register.jsp").forward(request, response); return; }
4,验证。
Struts2实现:
核心步骤:
(1) 在jsp页面写一个<s:token /> 标签。
(2) 在struts.xml中配置信息。配置拦截器,在当前的action中,加入defaultStack默认拦截器,在添加一个拦截器引用,引用token拦截器,使token拦截器追加到默认拦截器后面。在token中配置参数,参数名为includeMethods,用户说明这个token用于当前action类的那个方法,我们默认在注册方法register中。
(3) 设定错误处理信息,result接收的是invalid.token,再配置国际化文件,在当前Action所在目录中写一个properties文件,根据返回的英文信息,去default.properties中查询相应的键,在自定义的properties中将这个键用自己的信息覆盖即可。
实现原理:
基本与JavaEE的方式相同,首先,在jsp中定义一个<s:token />标签,当浏览器访问这个jsp时,<s:token />标签会自动生成一个随机数据,保存到服务器的session中,并在当前jsp中的一个隐藏域中记录这个随机生成的数据,当用户提交表单时,会将隐藏域中的数据带到服务器,服务器获取用户提交的信息,即这个随机码,再从服务器的session中获取数据。先判断服务器获取的数据是否是空,如果是空,则表明用户重复提交了数据,返回错误信息。如果不是空,则用户时第一次提交,先将服务器的session中的数据删除,以便于用户重复提交的时候session中的数据为空,再判断用户提交的数据与服务器获取的数据是否相等,如果相等,则注册成功,如果不相等,则回显“验证码输入错误”。