解决的问题
表单重复提交有三种常见的情况:
- 表单提交完成之后,服务器使用请求转发进行页面的跳转,此时,若用户按下 F5(刷新),浏览器会重新发起最后一次的请求,造成表单的重复提交。解决方法:使用请求重定向代替请求转发。
- 用户正常提交表单,但由于网络延迟等原因,迟迟未收到服务器的响应,此时,用户误以为表单提交失败,重复点击提交按钮,造成表单重复提交。解决方法:使用验证码。
- 用户正常提交表单,服务器也没有延迟,但提交完成后,用户回退浏览器页面(<- 按钮),再重新提交,也会造成表单重复
提交。解决方法:使用验证码。
kaptcha 的使用
实现
kaptcha 是谷歌提供的图片验证码,原理如下:
- 客户端访问验证码对应的 Servlet 程序,kaptcha 程序生成验证码,并将正确的验证码保存到 Session 域中,key 为:KAPTCHA_SESSION_KEY
- 接收客户端发送的验证码
- 取出 Session 域中的验证码,并 立即删除 Session 域中该验证码内容
- 比较两个验证码是否相同
每次访问验证码对应的 Servlet 程序后,kaptcha 会将正确验证码的内容保存到 Session 域中,当服务器接收客户端发送的验证码之后,
使用步骤如下:
- 导入谷歌 kaptcha 的 jar 包:kaptcha-2.3.2.jar
- 在 web.xml 中配置用于生成验证码的 Servlet 程序
上面的 KaptchaServlet.class 就是谷歌已经写好的用于生成验证码的 Servlet 程序,只需要在 web.xml 中配置该类的访问地址即可。
web.xml
<!-- 配置谷歌验证码程序KaptchaServlet -->
<servlet>
<servlet-name>KaptchaServlet</servlet-name>
<servlet-class>com.google.code.kaptcha.servlet.KaptchaServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>KaptchaServlet</servlet-name>
<url-pattern>/kaptcha.jpg</url-pattern>
</servlet-mapping>
- 在表单中使用 img 标签显示验证码图片
<form action="http://localhost:8080/tmp/registServlet" method="get">
用户名:<input type="text" name="username" > <br>
密码:<input type="password" name="password" > <br>
验证码:<input type="text" style="width: 80px;" name="code">
<img id="code_img" src="http://localhost:8080/tmp/kaptcha.jpg" alt="" style="width: 100px; height: 28px;"> <br>
<input type="submit" value="登录">
</form>
- 服务器端验证 Session 域中的验证码与客户端发来的验证码是否相同,并立即删除 Session 域中的验证码
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException,IOException {
// 获取 Session 中的验证码
String token = (String) req.getSession().getAttribute(Constants.KAPTCHA_SESSION_KEY);
// 立即删除 Session 中的验证码
req.getSession().removeAttribute(Constants.KAPTCHA_SESSION_KEY);
String code = req.getParameter("code");
// 获取用户名和密码
String username = req.getParameter("username");
String password = req.getParameter("password");
if (token != null && token.equalsIgnoreCase(code)) {
System.out.println("保存到数据库:" + username + ", " + password);
resp.sendRedirect(req.getContextPath() + "/login_successful.jsp");
} else {
System.out.println("请不要重复提交表单");
}
}
切换验证码
//为验证码图片绑定单击事件,使得每次点击验证码图片后都会刷新验证码
$("#code_img").click(function(){
/*
在事件响应的function函数中有一个this对象,这个this对象,就是当前正在响应事件的dom对象,
这里表示img标签对象,src属性表示img标签的图片路径,它可读可写
${ baseAddr }kaptcha.jpg = http://localhost:8080/tmp/kaptcha.jpg
浏览器缓存文件的名称由:资源名+参数 组成
增加一个可变参数d,使得每次请求的名称不同,避免浏览器缓存验证码,导致验证码点击时不刷新的问题
new Date().getTime()是JavaScript中提供的对象和方法
*/
this.src = "${ baseAddr }kaptcha.jpg?" + new Date().getTime();
});