编程语言
首页 > 编程语言> > javaweb之验证码

javaweb之验证码

作者:互联网

原文链接:http://www.cnblogs.com/huyongliang/p/4129548.html

  看了尚硅谷佟刚老师讲的httpsession应用中的验证码,总觉得一遍又一遍的写这些东西实在没意思,所有就简单封装了一个验证码生成器,默认支持纯数字、纯字母、数字字母组合、简单的10以内的加减乘的验证码。

  先声明:代码中BufferedImage等的生成部分和一些对验证码的修饰都是来自佟刚老师的代码!

  下载jar包:

  http://files.cnblogs.com/huyongliang/codeGenerator.zip  

  来个截图:

  

  

 

一 结构概览

  

  CheckCode类是这个工具包的抽象父类,提供了两个默认实现:

  1) DefaultCheckCode:支持数字、字母和数字加字母的验证码

  2) SimpleArithmeticExpressionCheckCode:简单的10以内加减乘的验证码

二 代码

 1. 抽象父类DefaultCheckCode.java

    这个类稍微有点长,其outline视图如下:

    

    对调用者提供的接口只有两个:

      1)getCheckCode(): public BufferedImage getCheckCode(HttpServletRequest request)

        返回生成的代表验证码的BufferedImage对象(并将正确的验证码写入session,键就是CHECK_CODE_KEY,可以自定义),拿到他之你就你就可以发送给客户端。

      2)isValid(): public static boolean isValid(HttpServletRequest request,String clientAnswer) 

        该方法用于判断客户端请求中的验证码是否合法。

全部代码如下(这是个抽象类,默认实现在后面,你可以自己实现他):

  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 }
View Code

 

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 }
View Code

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 }
View Code

三 生成验证码用法示例

  大多数的使用可能都是在控制器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     }
View Code

  1)纯数字: CheckCode checkCode = new DefaultCheckCode(CODE_TYPE.DIGIT); 

  2)纯字母: CheckCode checkCode = new DefaultCheckCode(CODE_TYPE.LETTER); 

  3)数字字母组合: CheckCode checkCode = new DefaultCheckCode(CODE_TYPE.DIGIT_LETTER); 

  4)自定义验证码序列来源: CheckCode checkCode = new DefaultCheckCode("12580ABC"); 从1、2、5、8、0、A、B、C中随机选择生成验证码。

  5)自定义验证码序列来源: 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 SimpleArithmeticExpressionCheckCode(); 

四 验证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 SimpleArithmeticExpressionCheckCode()); 用这一行代码你就能发送一个验证码到客户端了。

  代码如下:

 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 }
View Code

 

  好了,不怎么会排版,有点乱。

  欢迎纠错。

转载于:https://www.cnblogs.com/huyongliang/p/4129548.html

标签:javaweb,CheckCode,DefaultCheckCode,request,验证码,import,public
来源: https://blog.csdn.net/weixin_30726161/article/details/98083723