首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 软件管理 > 软件架构设计 >

应用jcaptcha在Struts中生成验证码

2012-11-03 
使用jcaptcha在Struts中生成验证码jcaptcha, 是一个Java开源项目, 为Java Completely Automated Public Te

使用jcaptcha在Struts中生成验证码
jcaptcha, 是一个Java开源项目, 为Java Completely Automated Public Test to tell Computers and Humans Apart 的简写, 字面意思就是Java实现的可以区分计算机和人的测试.
具体的测试方法有很多种, 比如:
验证码 - (这里讨论的)
音频 - (比如朗读几个字母, 用户输入这些字母来验证)

JCAPTCHA在Struts中配置很简单, 主要是以下三个部分:

1. 需要一个Action或者Servlet来作为页面的接口. 在访问该Acton或Servlet的时候, 调用(2) 生成随机的验证对象, 和当前的Session关联起来;
2. 一个JCAPTCHA Service类. 这个类完成读取配置(如何生成验证码, 生成的内容是什么, 等等), 并实际生成验证码. 同时也对外提供验证用户输入的功能.
3. 其它部分. 比如在UI层需要调用(1)来显示验证码图片, 在验证部分要调用(2)来测试输入的验证码是否正确.

下面是代码.

struts-config.xml:

<action path="/jcaptcha" type="test.CaptchaAction" />

与之对应的JSP部分. 很简单, 一个验证码图片, 一个输入框, 一个提交按钮.

<body>
       <img src="<%= request.getContextPath() %>/jcaptcha.do"/>
      <form method="POST" action="<%= request.getContextPath() %>/test.do?state=verify">
           <input name="code"/>
            <input type="submit"/>
      </form>
</body>

加入一个Action, 作用就是生成图片(写成一个Servlet也能完成一样的功能). Java代码为:

public class CaptchaAction extends Action {


    public ActionForward execute(ActionMapping mapping, ActionForm form,

           HttpServletRequest request, HttpServletResponse response)

           throws Exception {

        try {  

            CaptchaService.getInstance().writeCaptcha(request, response);  

        } catch (Exception e) {  

        e.printStackTrace();

        return mapping.findForward("fatal");  

        }  

       return super.execute(mapping, form, request, response);

    }

}


下面的就是重要的单例类CaptchaService了. 这个类继承自ListImageCaptchaEngine, 是JCAPTCAH的针对图片验证的类. CaptchaService类中有三个方法比较重要:

1. private buildInitialFactories()

这个方法是ListImageCaptchaEngine的abstract方法的覆盖. 必须实现. 目的是初始化验证码生成器的参数.


    protected void buildInitialFactories() {  

        try {  

            SimpleTextPaster parser = (new SimpleTextPaster(MIN_LENGTH, MAX_LENGTH, Color.black));  

        //RandomTextPaster parser = new RandomTextPaster(MIN_LENGTH, MAX_LENGTH, Color.black);  

           

            //UniColorBackgroundGenerator back = new UniColorBackgroundGenerator(IMAGE_WIDTH, IMAGE_HEIGHT);

            GradientBackgroundGenerator back = (new GradientBackgroundGenerator(IMAGE_WIDTH, IMAGE_HEIGHT, Color.orange, Color.white));  


            Font f = Font.decode("Consolas");

            RandomFontGenerator rf = (new RandomFontGenerator(MIN_FONT_SIZE, MAX_FONT_SIZE, new Font[]{f}));// to easy to read


            WordGenerator words = new RandomWordGenerator("ABCDEFG");

           

            WordToImage word2image = new ComposedWordToImage(rf, back, parser);  

            ImageCaptchaFactory factory = new GimpyFactory(words, word2image);  

            addFactory(factory);              

        } catch (Exception ex) {  

        ex.printStackTrace();

        }  

    }




对上面程序的解释. 一个验证码图片由四部分组成: TestParser, BackgroundGenerator, FontGenerator, 以及WordGenerator


TestParser, 都是从类AbstractTextPaster继承来, 作用是生成验证码中的文字部分. 较常用的下面两种:

- SimpleTextPaster 简单的实现. 文字部分是从左到右顺序排列的. 构造函数的参数分别为最短长度, 最大长度和文字颜色.
- RandomTextPaster 带有随机式样的实现. 和SimpleTextPaster的参数类似. 不过生成的文字部分, 带有随机的样式(粗体,斜体等), 开始位置也是随机的(可能从图片中间开始排列)


BackgroundGenerator, 都是从AbstractBackgroundGenerator类继承而来. 作用是产生验证码图片的背景. 构造的时候传入的基本参数为图片的宽度和高度. 常用的实现为:

- UniColorBackgroundGenerator 单一背景色. 默认的是白色.

- GradientBackgroundGenerator 渐变背景色. 在构造的时候需要指定起始颜色和终止颜色.


FontGenerator, 从AbstractFontGenerator 继承来. 但是和上面两种不同, 基本上在应用中使用的都是RandomFontGenerator. 构造的时候, 传入字体的最小值, 最大值, 以及字体列表的名称. 在生成验证码的时候, 随机选择某种字体, 某种大小来生成. 这里有几个问题需要注意:



FontGenerator字体的大小, TestParser中验证码的个数, 以及BackgroundGenerator中图片的大小需要匹配. 如果设置的不合理, 比如字体过大, 生成若干个验证码后发现图片的大小太小装不下了, 将出现异常.



在选择字体的时候, 下面两种名称开头字体是JCAPTCHA禁止使用的: Courier 和 Times Roman . 因为这两种字体用的太广泛了, 同时也是比较标准的字体, JCAPTCHA认为它们不够安全(容易被识别程序识别出来), 所以将忽略这些字体. 如果没有设置其他的字体的话, 会报错(找不到可用的字体).


WordGenerator: 生成文字的选项. 基本上用的都是RandomWordGenerator随机生成. 构造参数为可选的列表. 生成验证码的时候, 将从这些文字中选择.


这四项确定以后, 就可以生成一个factory, 加入生成列表里面去:


            WordToImage word2image = new ComposedWordToImage(rf, back, parser);  

            ImageCaptchaFactory factory = new GimpyFactory(words, word2image);  

            addFactory(factory);        


同样的, 可以有多个TestParser, BackgroundGenerator, FontGenerator, 生成多个factory, 这样每次生成验证码图片的时候都会更加不同.


2. writeCaptcha方法. 该方法的作用就是实际将图片写入response, 最终显示到浏览器上.

没什么特殊的. 唯一一点是, 通过setAttribute方法在当前的session中保存了验证码.



    public void writeCaptcha(HttpServletRequest request, HttpServletResponse response) throws IOException {  
        //保存验证码
        imageCaptcha = getNextImageCaptcha();  
        HttpSession session = request.getSession();  
        session.setAttribute(KEY, imageCaptcha);  
        BufferedImage image = (BufferedImage) imageCaptcha.getChallenge();  

        OutputStream outputStream = null;  
        try {  
            outputStream = response.getOutputStream();  
            response.setHeader("Cache-Control", "no-store");  
            response.setHeader("Pragma", "no-cache");  
            response.setDateHeader("Expires", 0);  
            response.setContentType("image/jpeg");  

            JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(outputStream);  
            encoder.encode(image);  

            outputStream.flush();  
        } catch (IOException ex) {  
            throw ex;  
        } finally {  
            if (outputStream != null) {  
                try {  
                    outputStream.close();  
                } catch (IOException ex) {  
                }  
            }  
            imageCaptcha.disposeChallenge();  
        }  
    } 


3. 验证方法. 验证用户输入的验证码是否正确.
很简单的一个方法.

    public boolean validateCaptcha(String validateCode, HttpSession session) {  
        try {  
        if (session == null) {
           return false;
        }
            imageCaptcha = (ImageCaptcha) session.getAttribute(KEY);  
            if (imageCaptcha == null) {  
               return false;  
            }  
            validateCode = validateCode.toUpperCase();
            boolean flag = (imageCaptcha.validateResponse(validateCode)).booleanValue();  
            session.removeAttribute(KEY);  
            return flag;  
        } catch (Exception ex) {  
            ex.printStackTrace();  
            return false;  
        }  
    }


剩下的 Action 部分, 加入判断:


public ActionForward verify(ActionMapping mapping, ActionForm form,
    HttpServletRequest request, HttpServletResponse response)
    throws Exception {
        String code = request.getParameter("code");
        boolean b = CaptchaService.getInstance().validateCaptcha(code, request.getSession());
        if (b) {

                // 正确

        } else {

               // 错误

        }
}



P.S: 中文的验证码是能够实现的, 只需要把上面的代码中字体一部分改成

            Font f = Font.decode("华文细黑"); //当然其他字体也可以, 但是必须是Server上有的字体

            WordGenerator words = new RandomWordGenerator("一二三四五六");

热点排行