看了尚硅谷佟刚老师讲的httpsession应用中的验证码,总觉得一遍又一遍的写这些东西实在没意思,所有就简单封装了一个验证码生成器,默认支持纯数字、纯字母、数字字母组合、简单的10以内的加减乘的验证码。
先声明:代码中BufferedImage等的生成部分和一些对验证码的修饰都是来自佟刚老师的代码!
下载jar包:
来个截图:
一 结构概览
CheckCode类是这个工具包的抽象父类,提供了两个默认实现:
1) DefaultCheckCode:支持数字、字母和数字加字母的验证码
2) SimpleArithmeticExpressionCheckCode:简单的10以内加减乘的验证码
二 代码
1. 抽象父类DefaultCheckCode.java
这个类稍微有点长,其outline视图如下:
对调用者提供的接口只有两个:
public
并将正确的验证码写入session,键就是CHECK_CODE_KEY,可以自定义),拿到他之你就你就可以发送给客户端。
public static boolean
该方法用于判断客户端请求中的验证码是否合法。
全部代码如下(这是个抽象类,默认实现在后面,你可以自己实现他):
1 package org.hyl.utils.code;
2
3 import java.awt.Color;
4 import java.awt.Font;
5 import java.awt.Graphics2D;
6 import java.awt.image.BufferedImage;
7 import java.util.Random;
8
9 import javax.servlet.http.HttpServletRequest;
10 import javax.servlet.http.HttpSession;
11
12 public abstract class CheckCode {
13 protected static String CHECK_CODE_KEY = "CHECK_CODE_KEY";
14
15 protected int width = 152;
16 protected int height = 40;
17 protected int codeCount = 4;
18
19 // 验证码字体的高度
20 protected int fontHeight = 4;
21
22 // 验证码中的单个字符基线. 即:验证码中的单个字符位于验证码图形左上角的 (codeX, codeY) 位置处
23 protected int codeX = 0;
24 protected int codeY = 0;
25
26
27
28 public CheckCode() {
29 this.width = 152;
30 this.height = 40;
31 this.init();
32 }
33
34 private void init() {
35 fontHeight = height - 2;
36 codeX = width / (codeCount + 2);
37 codeY = height - 4;
38 }
39
40 protected abstract String getPrintString();
41
42 protected abstract String getRightAnswer();
43
44 public BufferedImage getCheckCode(HttpServletRequest request) {
45 this.init();
46 char[] codeSequence = this.getPrintString().toCharArray();
47 int len = codeSequence.length;
48 BufferedImage image = new BufferedImage(width, height,
49 BufferedImage.TYPE_3BYTE_BGR);
50
51 this.renderImg(image);
52
53 Graphics2D graphics = image.createGraphics();
54 this.setDefaultStyleOfCode(graphics);
55
56 for (int i = 0; i < len; i++) {
57 graphics.setColor(getRandomColor());
58 graphics.drawString(String.valueOf(codeSequence[i]), (i + 1)
59 * codeX, codeY);
60 }
61 String answer = this.getRightAnswer();
62
63 request.getSession().setAttribute(CHECK_CODE_KEY, answer);
64
65 return image;
66 }
67
68 protected Color getRandomColor() {
69 Random random = new Random();
70
71 Color c = new Color(random.nextInt(154) + 50, random.nextInt(154) + 50,
72 random.nextInt(154) + 50);
73 return c;
74 }
75
76 protected void renderImg(BufferedImage image) {
77 Graphics2D graphics = image.createGraphics();
78
79 // 设置一个颜色, 使 Graphics2D 对象的后续图形使用这个颜色
80 graphics.setColor(Color.WHITE);
81
82 // 填充一个指定的矩形: x - 要填充矩形的 x 坐标; y - 要填充矩形的 y 坐标; width - 要填充矩形的宽度; height
83 // - 要填充矩形的高度
84 graphics.fillRect(0, 0, width, height);
85
86 // 创建一个 Font 对象: name - 字体名称; style - Font 的样式常量; size - Font 的点大小
87 Font font = null;
88 font = new Font("", Font.BOLD, fontHeight);
89 // 使 Graphics2D 对象的后续图形使用此字体
90 graphics.setFont(font);
91
92 graphics.setColor(Color.BLACK);
93
94 // 绘制指定矩形的边框, 绘制出的矩形将比构件宽一个也高一个像素
95 graphics.drawRect(0, 0, width - 1, height - 1);
96
97 // 随机产生 15 条干扰线, 使图像中的认证码不易被其它程序探测到
98 Random random = null;
99 random = new Random();
100 graphics.setColor(Color.GREEN);
101 for (int i = 0; i < 15; i++) {
102 int x = random.nextInt(width);
103 int y = random.nextInt(height);
104 int x1 = random.nextInt(20);
105 int y1 = random.nextInt(20);
106 graphics.drawLine(x, y, x + x1, y + y1);
107 }
108 }
109
110 protected void setDefaultStyleOfCode(Graphics2D graphics) {
111 Font font = null;
112 font = new Font("", Font.BOLD, fontHeight);
113 // 使 Graphics2D 对象的后续图形使用此字体
114 graphics.setFont(font);
115 }
116
117 public enum CODE_TYPE {
118 DIGIT, LETTER, DIGIT_LETTER
119 }
120
121 public static boolean isValid(HttpServletRequest request,
122 String clientAnswer) {
123 if (clientAnswer == null || clientAnswer.equals(""))
124 return false;
125 HttpSession sessioin = request.getSession(false);
126 if (sessioin == null)
127 return false;
128
129 String code = (String) sessioin.getAttribute(CHECK_CODE_KEY);
130 if (code == null || code == "")
131 return false;
132 if (code.equalsIgnoreCase(clientAnswer)) {
133 sessioin.removeAttribute(CHECK_CODE_KEY);
134 return true;
135 }
136 return false;
137 }
138 }
2. 默认实现类DefaultCheckCode.java(数字、字母、数字加字母)
1 package org.hyl.utils.code.iml;
2 import java.util.Random;
3 import org.hyl.utils.code.CheckCode;
4 public class DefaultCheckCode extends CheckCode {
5
6 private char[] codeSequence;
7 private String rightAnswer;
8
9 public DefaultCheckCode(CODE_TYPE codeType) {
10 switch (codeType) {
11 case DIGIT:
12 this.codeSequence = "0123456789".toCharArray();
13 break;
14 case LETTER:
15 this.codeSequence = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
16 .toCharArray();
17 break;
18 case DIGIT_LETTER:
19 this.codeSequence = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz23456789"
20 .toCharArray();
21 break;
22 default:
23 break;
24 }
25 }
26
27 public DefaultCheckCode(String userCodeSequence) {
28 if (userCodeSequence == null || userCodeSequence.equals(""))
29 throw new RuntimeException("用于生产验证码的序列不合法");
30 this.codeSequence = userCodeSequence.toCharArray();
31 }
32
33 public DefaultCheckCode(char[] codeSequence) {
34 this.codeSequence = codeSequence;
35 }
36
37 @Override
38 protected String getPrintString() {
39 Random random = new Random();
40 StringBuilder builder = new StringBuilder();
41 int len = this.codeSequence.length;
42 for (int i = 0; i < this.codeCount; i++) {
43 builder.append(this.codeSequence[random.nextInt(len)]);
44 }
45 this.rightAnswer = builder.toString();
46 return builder.toString();
47 }
48
49 @Override
50 protected String getRightAnswer() {
51 if (this.rightAnswer == null || this.rightAnswer.equals(""))
52 this.getPrintString();
53 return this.rightAnswer;
54 }
55
56 //以下三个方法可用于链式编程
57 public DefaultCheckCode setWidth(int width) {
58 this.width = width;
59 return this;
60 }
61
62 public DefaultCheckCode setHeight(int height) {
63 this.height = height;
64 return this;
65 }
66
67 public DefaultCheckCode setCodeCount(int c) {
68 this.codeCount = c;
69 return this;
70 }
71 }
3. 另一个实现类SimpleArithmeticExpressionCheckCode(简单数学表达式)
1 package org.hyl.utils.code.iml;
2
3 import java.util.Random;
4
5 import org.hyl.utils.code.CheckCode;
6
7 public class SimpleArithmeticExpressionCheckCode extends CheckCode {
8 private String printString;
9 private String rightAnswer;
10
11 private int num1;
12 private int num2;
13 private char op;
14
15 public SimpleArithmeticExpressionCheckCode() {
16
17 this.initOperationParams();
18 this.printString = new StringBuilder().append(num1).append(op)
19 .append(num2).append("=?").toString();
20 this.rightAnswer = this.executeCalculte() + "";
21 }
22
23 private void initOperationParams() {
24 Random random = new Random();
25 this.op = (new char[] { 'x', '-', '+', 'X' })[random.nextInt(4)];
26 this.num1 = random.nextInt(10);
27 this.num2 = random.nextInt(10);
28 }
29
30 private int executeCalculte() {
31 switch (this.op) {
32 case '+':
33 return this.num1 + this.num2;
34 case '-':
35 return this.num1 - this.num2;
36 case 'x':
37 case 'X':
38 return this.num1 * this.num2;
39 default:
40 return 0;
41 }
42 }
43
44 @Override
45 protected String getPrintString() {
46 return this.printString;
47 }
48
49 @Override
50 protected String getRightAnswer() {
51 return rightAnswer;
52 }
53
54 }
三 生成验证码用法示例
servlet中的,至于框架的话,我想你也会搞定。
1 package org.hyl.servlet;
2
3 import java.awt.image.BufferedImage;
4 import java.io.IOException;
5
6 import javax.imageio.ImageIO;
7 import javax.servlet.ServletException;
8 import javax.servlet.ServletOutputStream;
9 import javax.servlet.http.HttpServlet;
10 import javax.servlet.http.HttpServletRequest;
11 import javax.servlet.http.HttpServletResponse;
12
13 import org.hyl.utils.code.CheckCode;
14 import org.hyl.utils.code.CheckCode.CODE_TYPE;
15 import org.hyl.utils.code.iml.DefaultCheckCode;
16
17 public class GerCheckCodeServlet extends HttpServlet {
18 private static final long serialVersionUID = 1L;
19
20 protected void doGet(HttpServletRequest request,
21 HttpServletResponse response) throws ServletException, IOException {
22 // 拿到验证码类的实现类的对象
23
24 // ==================start=====================
25 //DIGIT_LETTER-----数字加字母
26 CheckCode checkCode = new DefaultCheckCode(CODE_TYPE.DIGIT_LETTER);
27 // ==================end=====================
28
29
30 // 获得将要发往Client的BufferedImage对象
31 BufferedImage image = checkCode.getCheckCode(request);
32
33 // 禁止图像缓存
34 response.setHeader("Pragma", "no-cache");
35 response.setHeader("Cache-Control", "no-cache");
36 response.setDateHeader("Expires", 0);
37
38 // 将图像输出到输出流中
39 ServletOutputStream sos = null;
40 sos = response.getOutputStream();
41 ImageIO.write(image, "jpeg", sos);
42 sos.close();
43 }
44
45 protected void doPost(HttpServletRequest request,
46 HttpServletResponse response) throws ServletException, IOException {
47 this.doGet(request, response);
48 }
49
50 }
无论使用哪个实现类,直接将上一个代码块的注释中的start到end部分替换即可。
1. DefaultCheckCode
上面一个代码块是数字加字母的验证码的使用。
对于DefaultCheckCode这个实现类的使用,你可以看看他的构造方法:
public DefaultCheckCode(CODE_TYPE codeType){
//....
}
public DefaultCheckCode(String userCodeSequence) {
//....
}
public DefaultCheckCode(char[] codeSequence) {
//...
}
直接将上一个代码块的注释start到end部分替换即可):
以下的前三个方法都需传入一个在父类中定义的枚举类型:
1 public enum CODE_TYPE {
2 DIGIT,//纯数字
3 LETTER,//纯字母
4 DIGIT_LETTER//数字加字母
5 }
纯数字: CheckCode checkCode = new DefaultCheckCode(CODE_TYPE.DIGIT);
纯字母: CheckCode checkCode = new DefaultCheckCode(CODE_TYPE.LETTER);
数字字母组合: CheckCode checkCode = new DefaultCheckCode(CODE_TYPE.DIGIT_LETTER);
自定义验证码序列来源: CheckCode checkCode = new 从1、2、5、8、0、A、B、C中随机选择生成验证码。
自定义验证码序列来源: CheckCode checkCode = new DefaultCheckCode(new char[]{'a','C','1'}); 从a、C、1中随机选择生成验证码。
6)可以自己定义验证码的宽度高度和个数的(有点链式编程的风格):
/*创建对象并封盖默认的高度宽度和字符个数*/ CheckCode checkCode = new DefaultCheckCode(CODE_TYPE.DIGIT) .setCodeCount(6) .setHeight(50) .setWidth(140);
2.SimpleArithmeticExpressionCheckCode(或许这才是你感兴趣的)
CheckCode checkCode = new
四 验证Client发送的验证码是否正确
验证成功则返回true,并将session中的键为CHECK_CODE_KEY的属性移除
表单:
1 <form action="<%=request.getContextPath()%>/GerCheckCodeServlet">
2 UserName:<input type="text" name="userName">
3 <br>
4 CheckCode:<input type="text" name="inputCode">
5 <img src="<%=request.getContextPath()%>/ValidateColorServlet">
6 <br>
7 <input type="submit" value="submit">
8 </form>
用于验证验证码正确性的servlet
1 protected void doGet(HttpServletRequest request,
2 HttpServletResponse response) throws ServletException, IOException {
3 String inputCode = request.getParameter("inputCode");
4 boolean isValid = CheckCode.isValid(request, inputCode);
5 if (isValid) {// 验证成功
6 //....
7 } else {// 验证失败
8 //....
9 }
10 }
五 说明
1. 对于自己写实现的说明:
你可以很容易实现你自己的实现类,只需要实现两个方法:
这个方法让你提供一个已经生成的验证码序列,就是将要写到BufferedImage中的验证码字符串。
protected abstract String getPrintString();
这个方法是让你提供验证码的正确答案,当然,对应传统的验证码,比如DefaultCheckCode,看到的就是正确答案;
但是对应项数学表达式的话,这个方法应该返回一个计算结果比如1+2=3,你该返回3。getPrintString应该返回1+2。
protected abstract String getRightAnswer();
2.有一个工具类供你使用
如果你实在是懒得不行的话,那么这里有个工具类让你使用:
CheckCodeUtil.sendCheckCodeToClient(request, response, new
代码如下:
1 package org.hyl.utils.code;
2
3 import java.awt.image.BufferedImage;
4 import java.io.IOException;
5
6 import javax.imageio.ImageIO;
7 import javax.servlet.ServletOutputStream;
8 import javax.servlet.http.HttpServletRequest;
9 import javax.servlet.http.HttpServletResponse;
10
11 /**
12 * 针对于{@link CheckCode}的实现类的工具类
13 *
14 * @author HuYongliang
15 *
16 */
17 public class CheckCodeUtil {
18 /**
19 * 发送参数code生产的验证码到response对应的客户端,并将验证码加入session中
20 *
21 * @param request
22 * @param response
23 * @param code
24 */
25 public static void sendCheckCodeToClient(HttpServletRequest request,
26 HttpServletResponse response, CheckCode code) {
27 BufferedImage image = code.getCheckCode(request);
28 // 禁止图像缓存
29 response.setHeader("Pragma", "no-cache");
30 response.setHeader("Cache-Control", "no-cache");
31 response.setDateHeader("Expires", 0);
32
33 // 将图像输出到输出流中
34 ServletOutputStream sos = null;
35 try {
36 sos = response.getOutputStream();
37 ImageIO.write(image, "jpeg", sos);
38 } catch (IOException e) {
39 e.printStackTrace();
40 } finally {
41 try {
42 sos.close();
43 } catch (IOException e) {
44 e.printStackTrace();
45 }
46 }
47 }
48
49 /**
50 * 判断client传送的验证码是否正确
51 *
52 * @param request
53 * @param clientAnswer
54 * @param checkCode
55 * @return
56 */
57 public static boolean isValid(HttpServletRequest request,
58 String clientAnswer, CheckCode checkCode) {
59 return CheckCode.isValid(request, clientAnswer);
60 }
61
62 /**
63 * 判断client传送的验证码是否正确
64 *
65 * @param request
66 * @param clientAnswer
67 * @return
68 */
69 public static boolean isValid(HttpServletRequest request,
70 String clientAnswer) {
71 return CheckCode.isValid(request, clientAnswer);
72 }
73 }
好了,不怎么会排版,有点乱。
欢迎纠错。