zoukankan      html  css  js  c++  java
  • 验证码SimpleCaptcha

        想总结一下验证码(开源使用的验证码。当然,也可以自己去实现,不过有成熟的开源项目就用吧)的内容,然后看了几个开源项目,发现SimpleCaptcha使用比较简单,容易入手,就从它开始吧。

        接触SimpleCaptcha

        1.下载 SimpleCaptcha

        2.将下载的JAR包添加到项目中

        3.配置Servlet

            可使用的Servlet:StickyCaptchaServlet、SimpleCaptchaServlet、AudioCaptchaServlet。

            StickyCaptchaServlet和SimpleCaptchaServlet产生图片验证码,AudioCaptchaServlet产生语音验证码。

            StickyCaptchaServlet产生的验证码将保存在session中,刷新将不会重新创建。

            StickyCaptchaServlet are “sticky” to the user’s session: page reloads will render the same CAPTCHA instead of generating a new one.

        下面写一个例子,简单的配置servlet,页面展示产生的验证码。

    Servlet配置
     1     <servlet>
     2         <servlet-name>StickyCaptcha</servlet-name>
     3         <servlet-class>nl.captcha.servlet.StickyCaptchaServlet</servlet-class>
     4     </servlet>
     5     <servlet>
     6         <servlet-name>SimpleCaptcha</servlet-name>
     7         <servlet-class>nl.captcha.servlet.SimpleCaptchaServlet</servlet-class>
     8     </servlet>
     9     <servlet>
    10         <servlet-name>AudioCpatcha</servlet-name>
    11         <servlet-class>nl.captcha.servlet.AudioCaptchaServlet</servlet-class>
    12     </servlet>
    13 
    14     <servlet-mapping>
    15         <servlet-name>StickyCaptcha</servlet-name>
    16         <url-pattern>/sticky.png</url-pattern>
    17     </servlet-mapping>
    18     <servlet-mapping>
    19         <servlet-name>SimpleCaptcha</servlet-name>
    20         <url-pattern>/simple</url-pattern>
    21     </servlet-mapping>
    22     <servlet-mapping>
    23         <servlet-name>AudioCpatcha</servlet-name>
    24         <url-pattern>/audio</url-pattern>
    25     </servlet-mapping>
    展示页面
     1 <table>
     2         <tr valign="top">
     3             <td>Sticky:</td>
     4             <td><img src="http://localhost:8080/captcha/sticky.png" />
     5             </td>
     6         </tr>
     7         <tr valign="top">
     8             <td>Simple:</td>
     9             <td><img src="http://localhost:8080/captcha/simple" />
    10             </td>
    11         </tr>
    12         <tr valign="top">
    13             <td>Audio:</td>
    14             <td><audio id="audio" src="http://localhost:8080/captcha/audio"
    15                     controls="controls"> 浏览器不支持&lt;audio&gt; </audio></td>
    16         </tr>
    17     </table>

        为什么传了三张效果图?当然不是无聊,三张是依次刷新后的效果图。发现Sticky部分的验证码刷新后也改变了,和上面的说明不一致啊。然后试验在页面上只放一个Sticky的验证码,刷新的确没改变。这是为什么呢?观察图片发现每次刷新收Sticky的内容都是上一次Simple的内容,恍然大悟......他们都往session里存了内容,而且存的AttributeName都一样,所以被覆盖了。

        打印出session的内容后发现Sticky、Simple在session存入的名称是simpleCaptcha(常量Captcha.NAME);Audio存入的名称是audioCaptcha(常量AudioCaptcha.NAME),所以可以通过attributeName从session中取出对象并转化成Captcha或AudioCaptcha,然后通过getAnswer方法获取验证码的答案,或通过isCorrect(String answer)直接验证传入验证码是否在正确。

         使用中还发现StickyCaptchaServlet在初次载入时无法正常产生返回内容。在http://simplecaptcha.git.sourceforge.net/git/gitweb.cgi?p=simplecaptcha/simplecaptcha;a=commit;h=4c3a9eddfefabab6492c89f5c3887ca622ce4b0f上看到这个bug已经被修复,但是用过程中依旧出现。

        Sticky也不常用,按照目前的验证码习惯,采用的验证码都是刷新页面重新生成的方式。

        上面的例子中只是展示的基本的验证码的效果,没有做验证,下面实现验证用户输入内容时候正确。

    Form表单
     1 <form action="http://localhost:8080/captcha/answer.jsp" method="post">
     2         <table>
     3             <tr valign="top">
     4                 <td>Simple:</td>
     5                 <td><img src="http://localhost:8080/captcha/simple" />
     6                 </td>
     7                 <td><input type="text" name="simpleAnswer" />
     8                 </td>
     9             </tr>
    10 
    11             <tr valign="top">
    12                 <td>Audio:</td>
    13                 <td><audio id="audio" src="http://localhost:8080/captcha/audio"
    14                         controls="controls"> 浏览器不支持&lt;audio&gt; </audio>
    15                 </td>
    16                 <td><input name="audioAnswer" />
    17                 </td>
    18             </tr>
    19         </table>
    20         <input type="submit" value="提交" />
    21     </form>

        接收并判断验证码是否正确的JSP(蛋疼的JSP,在也不想用JSP了

    验证JSP
     1 <%
     2         Captcha captcha = (Captcha) session.getAttribute(Captcha.NAME);
     3         AudioCaptcha audioCaptcha = (AudioCaptcha) session
     4                 .getAttribute(AudioCaptcha.NAME);
     5         if (captcha.getAnswer()
     6                 .equals(request.getParameter("simpleAnswer"))) {
     7     %>
     8     <B> Simple:Correct!</B>
     9     <br />
    10     <%
    11         } else {
    12     %>
    13     <B> Simple:Wrong!</B>
    14     <br />
    15     <%
    16         }
    17         if (captcha.isCorrect(request.getParameter("audioAnswer"))) {
    18     %>
    19     <B> Audio:Correct!</B>
    20     <%
    21         } else {
    22     %>
    23     <B> Audio:Wrong!</B>
    24     <%
    25         }
    26     %>

        Audio也比较蛋疼,目前也不属于常用的,毕竟语音验证应用起来要求客户端必须有语音输出,而且对用户也是有要求啊,听不大好的话......

    ---------------------------------------------------------------------------------------------------------------------------------

        上面使用了simplecaptcha包自带的Servlet,实现了简单的验证码产生及验证,下面开始拓展 simplecaptcha实现自定义的验证码。

        编写Servlet接收验证码的请求,使用Captcha.build()产生验证码,将图片内容写到返回流中,so easy......

    产生验证码的方法
     1 @Override
     2     protected void doPost(HttpServletRequest req, HttpServletResponse resp)
     3             throws ServletException, IOException {
     4         // 简单的是设定图片长宽,添加内容
     5         Captcha captcha = new Captcha.Builder(120, 38).addText().build();
     6         resp.setContentType("image/jpeg");
     7         OutputStream os = resp.getOutputStream();
     8         ImageIO.write(captcha.getImage(), "JPEG", os);
     9         req.getSession().setAttribute("checkCode", captcha.getAnswer());
    10     }

        上面的代码将验证码的答案存放在session中,当用户输入验证码并提交后可将用户输入内容与session中的内容比较来验证是否正确。

        在深入一点,限制验证码的内容,去除0、1、l、O等容易混淆的内容,限定字体,设置背景等等等等......

        addBackground()使用默认的BackgroundProducer添加背景,addBackground(BackgroundProducer)可以通过实现BackgroundProducer接口提供自己的背景生成器。

        addBorder()在图片周围绘制一个但像素宽的边框。

        addNoise()、addNoise(NoiseProducer nProd)分别通过默认的和提供的NoiseProducer设置“噪音”。使用默认的将在图片中加入横线。

        gimp()、gimp(GimpyRenderer gimpy)通过默认的或者提供的GimpyRenderer去对图片进行模糊。

        addText()使用默认的TextProducer创建验证码内容。

        addText(TextProducer txtProd)使用提供的TextProducer创建验证码内容。

        addText(TextProducer txtProd,WordRenderer wRenderer)使用提供的TextProducer创建验证码内容,使用提供的WordRenderer渲染内容。

        addText(WordRenderer wRenderer)使用默认的TextProducer创建验证码内容,使用提供的WordRenderer渲染内容。

        build()创建Captcha对象。

        给出两个像样点的效果吧,自定义TextProducer及WordRanderer。

    EasyCharTextProducer
     1 public class EasyCharTextProducer implements TextProducer {
     2 
     3     private static final char[] chars = { 'a', 'c', 'd', 'e', 'f', 'h', 'j',
     4             'k', 'm', 'n', 'p', 'r', 's', 't', 'w', 'x', 'y', '3', '4', '5',
     5             '7', '8' };
     6 
     7     private static final int lastChar = chars.length;
     8 
     9     private int length;
    10 
    11     public EasyCharTextProducer() {
    12         this(5);
    13     }
    14 
    15     public EasyCharTextProducer(int length) {
    16         super();
    17         this.length = length;
    18     }
    19 
    20     public String getText() {
    21         Random random = new Random();
    22         StringBuilder sb = new StringBuilder(length);
    23         for (int i = 0; i < length; i++) {
    24             int r = random.nextInt(lastChar);
    25             sb.append(chars[r]);
    26         }
    27         return sb.toString();
    28     }
    29 
    30 }
    FixedWordRenderer
     1 public class FixedWordRenderer implements WordRenderer {
     2     // The text will be rendered 25%/5% of the image height/width from the X and
     3     // Y axes
     4     private static final double YOFFSET = 0.20;
     5     private static final double XOFFSET = 0.15;
     6     private static final int charDistance = 5;
     7     private static final Color DEFAULT_COLOR = Color.BLACK;
     8     private static final List<Font> DEFAULT_FONTS = new ArrayList<Font>();
     9 
    10     private final Color _color;
    11     private final List<Font> _fonts;
    12 
    13     static {
    14         DEFAULT_FONTS.add(new Font("Arial", Font.BOLD, 40));
    15         DEFAULT_FONTS.add(new Font("Courier", Font.BOLD, 40));
    16     }
    17 
    18     /**
    19      * Will render the characters in black and in either 40pt Arial or Courier.
    20      */
    21     public FixedWordRenderer() {
    22         this(DEFAULT_COLOR, DEFAULT_FONTS);
    23     }
    24 
    25     public FixedWordRenderer(Color color, List<Font> fonts) {
    26         _color = color != null ? color : DEFAULT_COLOR;
    27         _fonts = fonts != null ? fonts : DEFAULT_FONTS;
    28     }
    29 
    30     /**
    31      * Render a word onto a BufferedImage.
    32      * 
    33      * @param word
    34      *            The word to be rendered.
    35      * @param bi
    36      *            The BufferedImage onto which the word will be painted on to
    37      */
    38     public void render(String word, BufferedImage image) {
    39         Random random = new Random();
    40         Graphics2D g = image.createGraphics();
    41 
    42         RenderingHints hints = new RenderingHints(
    43                 RenderingHints.KEY_ANTIALIASING,
    44                 RenderingHints.VALUE_ANTIALIAS_ON);
    45         hints.add(new RenderingHints(RenderingHints.KEY_RENDERING,
    46                 RenderingHints.VALUE_RENDER_QUALITY));
    47         g.setRenderingHints(hints);
    48 
    49         g.setColor(_color);
    50         FontRenderContext frc = g.getFontRenderContext();
    51         int startPosX = (int) Math.round(image.getWidth() * XOFFSET);
    52         int startPosY = image.getHeight()
    53                 - (int) Math.round(image.getHeight() * YOFFSET);
    54         char[] wc = word.toCharArray();
    55         for (char element : wc) {
    56             char[] itchar = new char[] { element };
    57             int choiceFont = random.nextInt(_fonts.size());
    58             Font itFont = _fonts.get(choiceFont);
    59             g.setFont(itFont);
    60 
    61             GlyphVector gv = itFont.createGlyphVector(frc, itchar);
    62             double charWitdth = gv.getVisualBounds().getWidth();
    63 
    64             g.drawChars(itchar, 0, itchar.length, startPosX, startPosY);
    65             startPosX = startPosX + (int) charWitdth
    66                     + random.nextInt(charDistance) - 2;
    67         }
    68     }
    69 
    70 }

        创建Captcha的代码及对应效果

    1 Captcha captcha = new Captcha.Builder(120, 38)
    2                 .addText(new EasyCharTextProducer(4),
    3                         new FixedWordRenderer(Color.white, englishFonts))
    4                 .addNoise()
    5                 .addNoise(new CurvedLineNoiseProducer(Color.gray, 1.5f))
    6                 .addBorder().addBackground(new GradiatedBackgroundProducer()).build();

    1 Captcha captcha = new Captcha.Builder(120, 38)
    2                 .addText(new EasyCharTextProducer(4),
    3                         new FixedWordRenderer(Color.white, englishFonts))
    4                 .gimp(new FishEyeGimpyRenderer()).addBorder().build();

        要更好的效果就需要去了解gimpy、noise、background等包下面提供的randerer、producer的效果以及自己去实现接口提供自己的randerer和producer,当然,审美很重要...... 

  • 相关阅读:
    android Edittext自定义输入字符和类型
    让android webView使用系统默认浏览器内核直接解析,不弹出选择浏览器选项
    java对象中继承和变量初始化顺序浅析
    android判断pad还是手机
    我不知道自己想要什么
    计算机网络概述
    2020/2/27-28
    操作系统概述
    数据模型
    数据库系统概述
  • 原文地址:https://www.cnblogs.com/hzmark/p/SimpleCaptcha.html
Copyright © 2011-2022 走看看