使用 Servlet+Jsp 完成验证码主要有如下步骤
- 完成 HTML 页面的编写
- 完成生成随机验证码的 Servlet
- 完成刷新更换验证码的功能
- 完成验证验证码是否正确是 Servlet
1. Html 的编写
页面结构相对比较简单,主要有input-text,img,a三种元素组成,input-text用于用户输入验证码,img用于显示验证码,而通常页面中都会用a标签来保证用户能更好的输入验证码。
1 2 3 4
| 验证码: <input type="text" name="CheckCode"> <img alt="验证码" id="imagecode" src="<%=request.getContextPath()%>/servlet/ImageServlet"/> <a href="javascript:ReloadCode();">看不清楚,换一张</a>
|
效果如图

2. 完成生成随机验证码的 Servlet
我们使用 Servlet 来完成对应的功能,创建ImageServlet类并继承自HttpServlet类。编写doGet()方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
| package com.yuchi;
import java.awt.Color; import java.awt.Graphics; import java.awt.image.BufferedImage; import java.io.IOException; import java.util.Random;
import javax.imageio.ImageIO; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse;
public class ImageServlet extends HttpServlet { public void doGet(HttpServletRequest Request,HttpServletResponse Response) throws IOException { BufferedImage bi = new BufferedImage(68, 22, BufferedImage.TYPE_INT_RGB); Graphics g = bi.getGraphics(); Color c = new Color(200, 150, 255); g.setColor(c); g.fillRect(0, 0, 68, 22); char[] ch = "abcdefghijklmnopqrstuvwxyz0123456789".toCharArray(); Random r = new Random(); int len = ch.length,index; StringBuffer sb = new StringBuffer(); for(int i = 0; i < 4; i++) { index = r.nextInt(len); g.setColor(new Color(r.nextInt(200), r.nextInt(150), r.nextInt(255))); g.drawString(ch[index] + " ", (i * 15) + 3, 18); sb.append(ch[index]); } Request.getSession().setAttribute("PicCode", sb.toString()); ImageIO.write(bi, "JPG", Response.getOutputStream()); } }
|
2.5 关于doGet()方法中的内容详解(个人理解)
1
| BufferedImage bi = new BufferedImage(68, 22, BufferedImage.TYPE_INT_RGB);
|
查阅了 API,BufferedImage类似乎用于将图片存入缓存中,在这里我们调用的构造方法有三个参数,分别对应图片的宽高和创建的图像格式。此处的BufferedImage.TYPE_INT_RGB是一个类成员属性。详见 API

1 2 3 4 5 6 7 8
| Graphics g = bi.getGraphics();
Color c = new Color(200, 150, 255);
g.setColor(c);
g.fillRect(0, 0, 68, 22);
|
API 上说 “Graphics 类是所有图形上下文的抽象基类,允许应用程序在组件(已经在各种设备上实现)以及闭屏图像上进行绘制。 ”
这里我认为其类似于 MFC 里那个PDC,获取了屏幕之后,所有的操作都是基于它的,这里的Graphics也是一样,关于颜色,大小等操作都是基于它进行设置的。
即setColor()和fillRect()方法,前者的参数是一个Color类对象,该类有多重构造方法,而此处使用的是三个int数值,对应的是RGB数值大于 0 小于 256.而后者则用于填充指定的矩形,参数是坐标和宽高。
1 2 3 4
|
char[] ch = "abcdefghijklmnopqrstuvwxyz0123456789".toCharArray();
|
此处使用了toCharArray()方法将字符串转换为字符数组,如果不适用该方法,char[] ch是没有办法这样赋值的。
1 2
| Random r = new Random();
|
API 中将Random称为伪随机,并称Math.random()更容易使用= =!

1 2 3 4
| int len = ch.length, index;
StringBuffer sb = new StringBuffer();
|
定义了用于存储长度的len以及对应的下标index,并定义了StringBuffer类对象用来存储随机生成的验证码。
1 2 3 4 5 6 7 8 9 10 11 12
| for(int i = 0; i < 4; i++) { index = r.nextInt(len); g.setColor(new Color(r.nextInt(200), r.nextInt(150), r.nextInt(255))); g.drawString(ch[index] + " ",(i * 15) + 3, 18); sb.append(ch[index]); }
|
使用 for 循环生成 4 次随机字符,r.nextInt(int len)方法用于生成一个随机的 0~len 之间的int变量。后面的获取颜色中也是如此使用。
void drawString(String str, int x,int y)方法用于将指定文本绘制到图形中,参数分别对应指定的 String 文本,以及宽高。
最后利用StringBuffer.append()方法将生成的字符添加到类对象sb中。
1 2 3 4
| Request.getSession().setAttribute("PicCode", sb.toString());
ImageIO.write(bi, "JPG", Response.getOutputStream());
|
创建 session 并添加sb.toString()用于实现判断
最后一个方法是我觉得最迷的- -!ImageIO.write()方法,API 中有三种构造方法,用于将ImageWriter按指定格式以三种不同方式输出。

完成了如上代码的编写之后需要配置servlet打开web.xml添加如下代码。
1 2 3 4 5 6 7 8
| <servlet> <servlet-name>ImageServlet</servlet-name> <servlet-class>com.yuchi.ImageServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>ImageServlet</servlet-name> <url-pattern>/servlet/ImageServlet</url-pattern> </servlet-mapping>
|
部署后运行。

到此处为止,随机生成验证码的功能就完成了。
3. 完成刷新更换验证码的功能
事实上我们在页面中使用验证码时往往容易出现验证码看不清的情况,因此我们需要有一个用户按钮使得验证码可以刷新,此处我们给a标签写上JavaScript以完成刷新功能的实现。
1 2 3 4 5 6
| <script type="text/javascript"> function ReloadCode(){ var time = new Date(); document.getElementById("imagecode").src = "<%=request.getContextPath()%>/servlet/ImageServlet?d=" + time; } </script>
|
此处使用了document.getElementById()获取到img标签之后,直接修改其src属性即可完成刷新,但 IE 浏览器似乎有一个缓存功能会使得刷新并不能生效,因此需要增加一个时间作为参数,使得每一次刷新的内容都不相同,这样 IE 才不会认为此次刷新是不需要的。
到这里,整个用户界面就算完成了效果如图

4. 完成对验证码的验证功能
其实验证功能的原理很简单,取出之前存入Session中的验证码,与用户的填入的验证码对比即可。
新建LoginServlet类,同样继承自HttpServlet类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| package com.yuchi;
import java.io.IOException; import java.io.PrintWriter;
import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse;
public class LoginServlet extends HttpServlet { public void doPost(HttpServletRequest Request,HttpServletResponse Response) throws IOException { String PicCode = (String) Request.getSession().getAttribute("PicCode"); String CheckCode = Request.getParameter("CheckCode"); CheckCode = CheckCode.toLowerCase(); Response.setContentType("text/html;charset=gbk"); PrintWriter out=Response.getWriter(); if(CheckCode.equals(PicCode)) { out.print("正确!"); }else { out.print("错误!"); } out.flush(); out.close(); } }
|
这一次我们选择使用post传参,因此方法也变成了doPost()【实在因为我对 get 那种 URL 传参的方式有点厌恶……】。
在类的编写中需要注意到几个问题
因此在类中我们编写了一些用于解决这些问题的语句
1 2 3 4 5 6 7
| CheckCode=CheckCode.toLowerCase();
Response.setContentType("text/html;charset=gbk");
out.flush(); out.close();
|
编写对应的 Servlet 并更改 HTML 结构
1 2 3 4 5 6 7 8
| <servlet> <servlet-name>LoginServlet</servlet-name> <servlet-class>com.yuchi.LoginServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>LoginServlet</servlet-name> <url-pattern>/servlet/LoginServlet</url-pattern> </servlet-mapping>
|
1 2 3 4
| <form action="<%=request.getContextPath()%>/servlet/LoginServlet" method="post"> ...... <input type="submit" value="提交"> </form>
|
以上,一个基本的验证码功能就完成了。效果如图:
