思想:通过验证码的形式防止表单重复提交。
通过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中的数据为空,再判断用户提交的数据与服务器获取的数据是否相等,如果相等,则注册成功,如果不相等,则回显“验证码输入错误”。