• Java实现验证码的制作


    验证码概述

    为什么使用验证码?

      验证码(CAPTCHA)是一种全自动程序。主要是为了区分“进行操作的是不是人”。如果没有验证码机制,将会导致以下的问题:

    • 对特定网站不断进行登录,破解密码;
    • 对某个网站创建账户;
    • 对某个网站提交垃圾数据(灌水贴);
    • 对某个网站进行刷票。

    使用Servlet实现验证码

      一个验证码包含两个部分:图片和输入框。

     1 <script type="text/javascript">
     2     function reloadCode(){
     3         var time = new Date();
     4         // 给URL传递参数可以清空浏览器的缓存,让浏览器认为这是一个新的请求
     5         document.getElementById('safecode').src = '<%=request.getContextPath()%>/servlet/ImageServlet?d=' + time;
     6     }
     7 </script>    
     8 
     9 <form action="<%=request.getContextPath()%>/servlet/ValidateImageServlet"method="post">
    10         验证码:<img src="<%=request.getContextPath()%>/servlet/ImageServlet" alt="验证码" id="safecode">
    11     <input type="text" id="verifyCode" name="verifyCode" size="6" />
    12     <a href="javascript:reloadCode();">看不清楚</a><br>
    13     <input type="submit" value="登录" />
    14 </form>

      我们用ImageServlet实时生成图片。生成图片所需要的步骤如下:

    1 定义BufferedImage对象
    2 获得Graphics对象
    3 听过Random类产生随机验证码信息
    4 使用Graphics绘制图片
    5 记录验证码信息到session中
    6 使用ImageIO输出图片

      检验验证码是否正确:ValidateImageServlet

    1 获取页面的验证码
    2 获取session中保存的验证码
    3 比较验证码
    4 返回校验结果

      验证的流程如下:

      生成验证码的ImageServlet:

     1 private static Random r = new Random();
     2     private static char[] chs = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789".toCharArray();
     3     private static final int NUMBER_OF_CHS = 4;
     4     private static final int IMG_WIDTH = 65;
     5     private static final int IMG_HEIGHT = 25;
     6     
     7     
     8     public void doGet(HttpServletRequest request, HttpServletResponse response)
     9             throws ServletException, IOException {
    10 
    11             BufferedImage image = new BufferedImage(IMG_WIDTH, IMG_HEIGHT, BufferedImage.TYPE_INT_RGB);    // 实例化BufferedImage
    12             Graphics g = image.getGraphics();
    13             Color c = new Color(200, 200, 255);                                             // 验证码图片的背景颜色                                        
    14             g.setColor(c);
    15             g.fillRect(0, 0, IMG_WIDTH, IMG_HEIGHT);                                        // 图片的边框
    16             
    17             StringBuffer sb = new StringBuffer();                                           // 用于保存验证码字符串
    18             int index;                                                                      // 数组的下标
    19             for (int i = 0; i < NUMBER_OF_CHS; i++) {
    20                 index = r.nextInt(chs.length);                                              // 随机一个下标
    21                 g.setColor(new Color(r.nextInt(88), r.nextInt(210), r.nextInt(150)));       // 随机一个颜色
    22                 g.drawString(chs[index] + "", 15 * i + 3, 18);                              // 画出字符
    23                 sb.append(chs[index]);                                                      // 验证码字符串
    24             }
    25             
    26             request.getSession().setAttribute("piccode", sb.toString());                    // 将验证码字符串保存到session中
    27             ImageIO.write(image, "jpg", response.getOutputStream());                        // 向页面输出图像
    28     }
    29 
    30     public void doPost(HttpServletRequest request, HttpServletResponse response)
    31             throws ServletException, IOException {
    32         doGet(request, response);
    33     }
    34 
    35 }

      进行验证码图片验证的Servlet:

     1 public class ValidateImageServlet extends HttpServlet {
     2 
     3     public void doGet(HttpServletRequest request, HttpServletResponse response)
     4             throws ServletException, IOException {
     5         
     6         doPost(request, response);
     7     }
     8 
     9     public void doPost(HttpServletRequest request, HttpServletResponse response)
    10             throws ServletException, IOException {
    11 
    12         response.setContentType("text/html;charset=utf-8");
    13         String picString = (String) request.getSession().getAttribute("piccode");
    14         String checkCode = request.getParameter("verifyCode");
    15         PrintWriter out = response.getWriter();
    16         if (picString.toUpperCase().equals(checkCode.toUpperCase()))
    17             out.println("验证码正确");
    18         else
    19             out.print("验证码错误!");
    20         
    21         out.flush();
    22         out.close();
    23     }
    24 
    25 }

     开源组件实现验证码

      Jcaptcha:

      一个用来生成图形验证码的开源组件,可以产生多种形式的验证码。可以与Spring组合使用。需要导入的jar包如下:

      用于展示验证码的auth_code_captcha.jsp如下:

    1 <form action="submit.action" method="post">
    2      <img src="jcaptcha.jpg" /> <input type="text" name="japtcha" value="" />
    3      <input type="submit"/>
    4 </form>

      web.xml的配置如下:

     1 <servlet>
     2     <servlet-name>jcaptcha</servlet-name>
     3     <servlet-class>com.octo.captcha.module.servlet.image.SimpleImageCaptchaServlet</servlet-class>
     4 </servlet>
     5 <!-- 处理表单提交的Servlet -->
     6 <servlet>
     7     <servlet-name>submit</servlet-name>
     8     <servlet-class>org.gpf.servlet.SubmitActionServlet</servlet-class>
     9 </servlet>
    10 <servlet-mapping>
    11     <servlet-name>jcaptcha</servlet-name>
    12     <url-pattern>/jcaptcha.jpg</url-pattern>
    13 </servlet-mapping>
    14 <servlet-mapping>
    15     <servlet-name>submit</servlet-name>
    16     <url-pattern>/submit.action</url-pattern>
    17 </servlet-mapping>

      表单提交的Servlet:

     1 package org.gpf.servlet;
     2 
     3 import java.io.IOException;
     4 
     5 import javax.servlet.ServletException;
     6 import javax.servlet.http.HttpServlet;
     7 import javax.servlet.http.HttpServletRequest;
     8 import javax.servlet.http.HttpServletResponse;
     9 
    10 import com.octo.captcha.module.servlet.image.SimpleImageCaptchaServlet;
    11 /**
    12  * 图片验证码的captcha实现
    13  * @author gaopengfei
    14  * @date 2015-5-20 下午9:58:20
    15  */
    16 public class SubmitActionServlet extends HttpServlet {
    17     
    18     private static final long serialVersionUID = 1L;
    19 
    20     protected void doPost(HttpServletRequest request,
    21             HttpServletResponse response) throws ServletException, IOException {
    22         
    23         String userCaptchaResponse = request.getParameter("japtcha");
    24         boolean captchaPassed = SimpleImageCaptchaServlet.validateResponse(request, userCaptchaResponse);
    25         
    26         response.setContentType("text/html;charset=utf-8");
    27         if (captchaPassed)
    28             response.getWriter().write("验证通过!");
    29         else {
    30             response.getWriter().write("验证失败!");
    31         }
    32         response.getWriter().write("<br/><a href='auth_code_captcha.jsp'>重新验证</a>");
    33     }
    34 }

      

      kaptcha:

      它是可以配置的,也可以生成各种样式的验证码。如下是其简单应用:用于显示验证码的index.jsp

    1 <img alt="验证码图片" src="random.jpg">
    2 <form action="check.jsp" method="post">
    3     <input type="text" name="imageText">
    4     <input type="submit" value="验证">
    5 </form>

      用于验证验证码的check.jsp

     1 <%@ page import="com.google.code.kaptcha.Constants" %>
     2 <%@ page language="java" import="java.util.*" contentType="text/html; charset=utf-8"%>
     3 <%
     4     String myImageText = request.getParameter("imageText");
     5     String key = (String)request.getSession().getAttribute(com.google.code.kaptcha.Constants.KAPTCHA_SESSION_KEY);
     6     
     7     if(!myImageText.isEmpty() && myImageText.equals(key))
     8         out.print("验证通过!<br />");
     9     else
    10         out.print("验证失败!<br />");    
    11     out.print("你输入的字符:" + myImageText + ",验证码字符:" + key);    
    12  %>

      配置图片显示的Servlet:

    1 <servlet>
    2     <servlet-name>Kcaptcha</servlet-name>
    3     <servlet-class>com.google.code.kaptcha.servlet.KaptchaServlet</servlet-class>
    4 </servlet>
    5 <servlet-mapping>
    6     <servlet-name>Kcaptcha</servlet-name>
    7     <url-pattern>/random.jpg</url-pattern>
    8 </servlet-mapping>

      

    Kaptcha的详细配置

      1 <servlet>
      2     <servlet-name>Kcaptcha</servlet-name>
      3     <servlet-class>com.google.code.kaptcha.servlet.KaptchaServlet</servlet-class>
      4     
      5     <!-- 边框 -->
      6     <init-param>
      7         <description>图片边框,yes(默认值)或者no</description>
      8         <param-name>kaptcha.border</param-name>
      9         <param-value>yes</param-value>
     10     </init-param>
     11     <init-param>
     12         <description>边框颜色,white、black(默认)、blue等</description>
     13         <param-name>kaptcha.border.color</param-name>
     14         <param-value>green</param-value>
     15     </init-param>
     16     <init-param>
     17         <description>边框厚度大于0</description>
     18         <param-name>kaptcha.border.thickness</param-name>
     19         <param-value>10</param-value>
     20     </init-param>
     21     
     22     <!-- 图片宽高 -->
     23     <init-param>
     24         <description>图片宽度</description>
     25         <param-name>kaptcha.image.width</param-name>
     26         <param-value>200</param-value>
     27     </init-param>
     28     <init-param>
     29         <description>图片高度</description>
     30         <param-name>kaptcha.image.height</param-name>
     31         <param-value>60</param-value>
     32     </init-param>
     33 
     34     <!-- 图片样式 -->
     35     <init-param>
     36         <description>图片样式:水纹(WaterRipple)、鱼眼(FishEyeGimpy)、阴影(ShadowGimpy)</description>
     37         <param-name>kaptcha.obscurificator.impl</param-name>
     38         <param-value>com.google.code.kaptcha.impl.ShadowGimpy</param-value>
     39     </init-param>
     40     
     41     <!-- 背景 -->
     42     <init-param>
     43         <description>背景实现类</description>
     44         <param-name>kaptcha.background.impl</param-name>
     45         <param-value>com.google.code.kaptcha.impl.DefaultBackground</param-value>
     46     </init-param>
     47     <init-param>
     48         <description>背景颜色渐变,指定开始颜色</description>
     49         <param-name>kaptcha.background.clear.from</param-name>
     50         <param-value>yellow</param-value>
     51     </init-param>
     52     <init-param>
     53         <description>背景颜色渐变,指定结束颜色</description>
     54         <param-name>kaptcha.background.clear.to</param-name>
     55         <param-value>red</param-value>
     56     </init-param>
     57     
     58     <!-- 文本 -->
     59     <init-param>
     60         <description>文本集合,验证码文字从此集合中获取</description>
     61         <param-name>kaptcha.textproducer.char.string</param-name>
     62         <param-value>0123456789</param-value>
     63     </init-param>
     64     <init-param>
     65         <description>验证码长度</description>
     66         <param-name>kaptcha.textproducer.char.length</param-name>
     67         <param-value>6</param-value>
     68     </init-param>
     69     <init-param>
     70         <description>文字间隔</description>
     71         <param-name>kaptcha.textproducer.char.space</param-name>
     72         <param-value>2</param-value>
     73     </init-param>
     74     <!-- 字体 -->
     75     <init-param>
     76         <description>字体Arial,Courier</description>
     77         <param-name>kaptcha.textproducer.font.names</param-name>
     78         <param-value>Arial,Courier</param-value>
     79     </init-param>
     80     <init-param>
     81         <description>字体大小</description>
     82         <param-name>kaptcha.textproducer.font.size</param-name>
     83         <param-value>40</param-value>
     84     </init-param>
     85     <init-param>
     86         <description>字体颜色,white、black(默认)、blue等</description>
     87         <param-name>kaptcha.textproducer.font.color</param-name>
     88         <param-value>pink</param-value>
     89     </init-param>
     90 
     91     <init-param>
     92         <description>文字渲染器</description>
     93         <param-name>kaptcha.word.impl</param-name>
     94         <param-value>com.google.code.kaptcha.text.impl.DefaultWordRenderer</param-value>
     95     </init-param>
     96 
     97     <!-- 图片和文本的实现类 -->
     98     <init-param>
     99         <description>图片实现类,可以重写这个类实现我们自己的图片</description>
    100         <param-name>kaptcha.producer.impl</param-name>
    101         <param-value>com.google.code.kaptcha.impl.DefaultKaptcha</param-value>
    102     </init-param>
    103     <init-param>
    104         <description>文本实现类</description>
    105         <param-name>kaptcha.textproducer.impl</param-name>
    106         <param-value>com.google.code.kaptcha.text.impl.DefaultTextCreator</param-value>
    107     </init-param>
    108     
    109     <!-- 干扰 -->
    110     <init-param>
    111         <description>干扰实现类</description>
    112         <param-name>kaptcha.noise.impl</param-name>
    113         <param-value>com.google.code.kaptcha.impl.DefaultNoise</param-value>
    114     </init-param>
    115     <init-param>
    116         <description>干扰颜色,合法值r,g,b或者white、black、blue</description>
    117         <param-name>kaptcha.noise.color</param-name>
    118         <param-value>255,0,0</param-value>
    119     </init-param>
    120     
    121     <init-param>
    122         <description>session中存放验证码的key键</description>
    123         <param-name>kaptcha.session.key</param-name>
    124         <param-value>KAPTCHA_SESSION_KEY</param-value>
    125     </init-param>
    126 </servlet>
    127 <servlet-mapping>
    128     <servlet-name>Kcaptcha</servlet-name>
    129     <url-pattern>/random.jpg</url-pattern>
    130 </servlet-mapping>

      中文验证码

      查看前面的配置发现验证码字符的生成主要依靠的是kaptcha.textproducer.impl这个文本实现类,查看com.google.code.kaptcha.text.impl.DefaultTextCreator的源码,发现了它继承自 Configurable并实现了TextProducer接口。我们可以仿照它自定义我们自己的验证码中的文本生成器:

     1 /**
     2  * 中文验证码的实现类
     3  */
     4 public class ChineseTextCreator extends Configurable implements TextProducer {
     5 
     6     @Override
     7     public String getText() {
     8 
     9         int length = getConfig().getTextProducerCharLength();
    10         String finalWord = "", firstWord = "";
    11         int tempInt = 0;
    12         String[] array = { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
    13                 "a", "b", "c", "d", "e", "f" };
    14 
    15         Random rand = new Random();
    16 
    17         for (int i = 0; i < length; i++) {
    18             switch (rand.nextInt(array.length)) {
    19             case 1:
    20                 tempInt = rand.nextInt(26) + 65;
    21                 firstWord = String.valueOf((char) tempInt);
    22                 break;
    23             case 2:
    24                 int r1,
    25                 r2,
    26                 r3,
    27                 r4;
    28                 String strH,
    29                 strL;// high&low
    30                 r1 = rand.nextInt(3) + 11; // 前闭后开[11,14)
    31                 if (r1 == 13) {
    32                     r2 = rand.nextInt(7);
    33                 } else {
    34                     r2 = rand.nextInt(16);
    35                 }
    36 
    37                 r3 = rand.nextInt(6) + 10;
    38                 if (r3 == 10) {
    39                     r4 = rand.nextInt(15) + 1;
    40                 } else if (r3 == 15) {
    41                     r4 = rand.nextInt(15);
    42                 } else {
    43                     r4 = rand.nextInt(16);
    44                 }
    45 
    46                 strH = array[r1] + array[r2];
    47                 strL = array[r3] + array[r4];
    48 
    49                 byte[] bytes = new byte[2];
    50                 bytes[0] = (byte) (Integer.parseInt(strH, 16));
    51                 bytes[1] = (byte) (Integer.parseInt(strL, 16));
    52 
    53                 firstWord = new String(bytes);
    54                 break;
    55             default:
    56                 tempInt = rand.nextInt(10) + 48;
    57                 firstWord = String.valueOf((char) tempInt);
    58                 break;
    59             }
    60             finalWord += firstWord;
    61         }
    62         return finalWord;
    63     }
    64 }

      只需要在web.xml中将初始化参数由默认的文本实现类改成我们自己的实现类:

    1 <init-param>
    2     <description>文本实现类</description>
    3     <param-name>kaptcha.textproducer.impl</param-name>
    4     <param-value>ChineseTextCreator</param-value>
    5 </init-param>

      算式验证码

    实现步骤如下:

    • 获取随机的数值将结果相加
    • 将计算公式写入到验证码图片
    • 将相加的结果放入到session中

    因此,我们需要重写KaptchaServlet这个用于生成验证码的Servlet。

     1 public class MyKaptchaServlet extends HttpServlet implements Servlet{
     2     
     3     private Properties props;
     4     private Producer kaptchaProducer;
     5     private String sessionKeyValue;
     6     
     7     public MyKaptchaServlet() {
     8          props = new Properties();
     9          kaptchaProducer = null;
    10          sessionKeyValue = null;
    11     }
    12     
    13     public void init(ServletConfig conf) throws ServletException {
    14         super.init(conf);
    15 
    16         ImageIO.setUseCache(false);
    17 
    18         Enumeration initParams = conf.getInitParameterNames();
    19         while (initParams.hasMoreElements()) {
    20             String key = (String) initParams.nextElement();
    21             String value = conf.getInitParameter(key);
    22             this.props.put(key, value);
    23         }
    24 
    25         Config config = new Config(this.props);
    26         this.kaptchaProducer = config.getProducerImpl();
    27         this.sessionKeyValue = config.getSessionKey();
    28     }
    29 
    30     public void doGet(HttpServletRequest req, HttpServletResponse resp)
    31             throws ServletException, IOException {
    32         resp.setDateHeader("Expires", 0L);
    33 
    34         resp.setHeader("Cache-Control", "no-store, no-cache, must-revalidate");
    35 
    36         resp.addHeader("Cache-Control", "post-check=0, pre-check=0");
    37 
    38         resp.setHeader("Pragma", "no-cache");
    39 
    40         resp.setContentType("image/jpeg");
    41 
    42         String capText = this.kaptchaProducer.createText();
    43         String s1 = capText.substring(0, 1);    // 获取随机生成的第一个数字
    44         String s2 = capText.substring(1, 2);    // 由于web.xml中配置的验证码字符都是数字,不会发生数字格式化异常
    45         int r = Integer.parseInt(s1) + Integer.parseInt(s2);
    46 
    47         req.getSession().setAttribute(this.sessionKeyValue, String.valueOf(r));    // 将结果存入session
    48 
    49         BufferedImage bi = this.kaptchaProducer.createImage(s1 + " + " + s2 + " = ?"); // 产生图片
    50 
    51         ServletOutputStream out = resp.getOutputStream();
    52 
    53         ImageIO.write(bi, "jpg", out);
    54         try {
    55             out.flush();
    56         } finally {
    57             out.close();
    58         }
    59     }
    60 
    61 }

      在web.xml中有2点需要注意:

    1. 验证码文本只能是0~9这10个数字;
    2. 验证码的长度是2(写多了也只会取出前2个随机数,写少了会抛出数字格式化异常)。
     1 <servlet-name>Kcaptcha</servlet-name>
     2     <servlet-class>MyKaptchaServlet</servlet-class>
     3 <init-param>
     4         <description>文本集合</description>
     5         <param-name>kaptcha.textproducer.char.string</param-name>
     6         <param-value>0123456789</param-value>
     7     </init-param>
     8     <init-param>
     9     <description>验证码长度</description>
    10     <param-name>kaptcha.textproducer.char.length</param-name>
    11     <param-value>8</param-value>
    12 </init-param>

  • 相关阅读:
    第二次作业
    大学——新生活方式
    第四次作业
    第三次作业
    第二次作业——起航
    梦开始的地方
    第四次作业
    第三次作业
    第二次作业
    博客作业 随笔
  • 原文地址:https://www.cnblogs.com/happyfans/p/4486010.html
Copyright © 2020-2023  润新知