使用JCaptcha生成验证码

在项目里用JCaptcha添加了验证码. 中间调试费了很多时间, 这里直接把步骤和结果的源代码贴出来吧.

首先要到JCaptcha的官网去下载jar包, 目前使用 jcaptcha-1.0-all.jar 这个就够了, 如果你的lib中没有 commons-collections-3.2.1.jar 的话, 需要去 Apache Collections 网站下一个, 因为生成时使用的fasthashmap依赖于这个jar.

JCaptcha 官网上 java doc已经已经更新至2.0alpha版, 但是发布的jar还是1.0, 所以java doc中提到的一些engine在jar中是没有的, 一些deprecated的类在1.0中依然需要使用.

JCaptcha 官网上提供了一个最简单的5分钟速成样例, 直接使用DefaultManageableImageCaptchaService构建验证图.

因为默认的验证图很难辨认, 所以还是需要自己定制一下, 以适应自己项目的需求.

首先是扩展出自己的ImageCaptchaEngine, 命名为 CaptchaImageEngine.java . 这里借鉴了几个网络上前辈提供的例子, 做了一些修改

public class CaptchaImageEngine extends ListImageCaptchaEngine
{
    public static final Integer WORD_MIN_LENGTH = new Integer(4);
    public static final Integer WORD_MAX_LENGTH = new Integer(4);

    public static final Integer IMAGE_WIDTH = new Integer(160);
    public static final Integer IMAGE_HEIGHT = new Integer(50);

    public static final Integer FONT_MIN_SIZE = new Integer(30);
    public static final Integer FONT_MAX_SIZE = new Integer(35);

    protected void buildInitialFactories()
    {
        WordGenerator wordGenerator = (new RandomWordGenerator("23456789ABCDEFGHJKMNPQRSTUVWXY"));

        BackgroundGenerator backgroundGenerator = new UniColorBackgroundGenerator(IMAGE_WIDTH, IMAGE_HEIGHT);

        Font[] fontsList = new Font[]{Font.decode("Arial"), Font.decode("Georgia"), Font.decode("Verdana"), Font.decode("Courier New")};
        FontGenerator fontGenerator = new RandomFontGenerator(FONT_MIN_SIZE, FONT_MAX_SIZE, fontsList);

        RandomRangeColorGenerator cgen = new RandomRangeColorGenerator(
                new int[] { 0, 128 },
                new int[] { 0, 128 },
                new int[] { 0, 196 }
            );

        TextDecorator[] textdecorators = new TextDecorator[]{new BaffleTextDecorator(new Integer(1), Color.WHITE)};

        TextPaster textPaster = new DecoratedRandomTextPaster(
                WORD_MIN_LENGTH,
                WORD_MAX_LENGTH,
                cgen,
                textdecorators
            );

        WaterFilter water = new WaterFilter();
        water.setAmplitude(5d);//振幅
        water.setAntialias(true);//锯齿或平滑
        water.setPhase(30d);//相位
        water.setWavelength(60d);

        WordToImage wordToImage = new DeformedComposedWordToImage(
                fontGenerator,
                backgroundGenerator,
                textPaster,
                new ImageDeformationByFilters(new ImageFilter[]{}),
                new ImageDeformationByFilters(new ImageFilter[]{}),
                new ImageDeformationByFilters(new ImageFilter[]{water})
            );

        addFactory(new GimpyFactory(wordGenerator, wordToImage));
    }
}

然后是提供生成验证图的静态方法的 CaptchaService.java . 这里就可以使用刚才建的ImageEngine来绘制验证图了

public class CaptchaService
{
    private static ImageCaptchaService instance = new DefaultManageableImageCaptchaService(
            new FastHashMapCaptchaStore(),
            new CaptchaImageEngine(),
            180, 100000, 75000
        );

    private CaptchaService(){}

    public static BufferedImage getImageChallenge(String sid, Locale locale)
    {
        return instance.getImageChallengeForID(sid, locale);
    }

    public static BufferedImage getImageChallenge(String sid)
    {
        return instance.getImageChallengeForID(sid);
    }

    public static boolean validate(String sid, String input)
    {
        return instance.validateResponseForID(sid, input);
    }
}

这样, 在action中就可以直接调用了, 调用的方法分为两部分, 一部分是生成图片

public String doGenerate()
    {
        try
        {
            // 获取session中的token用于生成验证图片,
            String token = getSession().getSession_token();

            // 生成验证图片
            BufferedImage challenge = CaptchaService.getImageChallenge(token);

            // 压缩为JPEG格式
            ByteArrayOutputStream jpegOutputStream = new ByteArrayOutputStream();
            JPEGImageEncoder jpegEncoder = JPEGCodec.createJPEGEncoder(jpegOutputStream);
            jpegEncoder.encode(challenge);

            // 置入action result的输出数据中
            this.put_streambytes("image/jpeg", jpegOutputStream.toByteArray());
        }
        catch (IllegalArgumentException e)
        {
            this.put_streambytes("image/jpeg", new byte[]{});
            logger.info("IllegalArgumentException");
            logger.debug("Exception Details", e);
        }
        catch (Exception e)
        {
            this.put_streambytes("image/jpeg", new byte[]{});
            logger.info("UnknownException");
            logger.debug("Exception Details", e);
        }

        return SUCCESS;
    }

然后, 用于处理action result的方法, 会使用 ServletOutputStream 输出图片

public class StreamProcessor {

    private static Logger logger = Logger.getLogger(RawProcessor.class);

    public static void process(HttpServletResponse res, String type, Object streamBytes)
    {
        try
        {
            res.setHeader("Cache-Control", "no-store");
            res.setHeader("Pragma", "no-cache");
            res.setDateHeader("Expires", 0);
            res.setContentType(type);
            ServletOutputStream responseOutputStream = res.getOutputStream();
            responseOutputStream.write((byte[])streamBytes);
            responseOutputStream.flush();
            responseOutputStream.close();
        }
        catch (Exception e)
        {
            logger.info("Unknown Exception");
            logger.debug("Exception details:", e);
        }
    }
}

另一方面, 需要在页面上添加对图片的引用, 因为需要制造点击刷新的效果, 使用javascript来生成html引用代码.
前端的HTML是 (

 

其中使用到的回调函数是

function captcha_image(element_id, src) 
{
    var joiner = '&';
    if (src.indexOf('?')== -1) joiner = '?';
    $("#"+element_id).html(' ');
}

后端响应这段js的action, 其作用, 是返回一段js代码, 让浏览器端去调用captcha_image()这个方法来生成引用图片的HTML代码

public String doJsInclude()
    {
        DefaultRequestBean bean = new DefaultRequestBean();
        String elementId = bean.get("element");
        String output = "captcha_image(\""+elementId+"\",\""+baseLink("captcha_image")+"\");";
        this.put_raw_string(output);
        return SUCCESS;
    }

在用户提交后, 用于验证用户提交的验证码内容的方法片段:

            String token = getSession().getSession_token();
            if (!CaptchaService.validate(token, this.captcha_input.toUpperCase()))
            {
                flag = false;
                this.put_error_msg("captcha_input", langRes.get("login_success"));
            }
            return flag;

 


posted on 2012-04-02 03:01  Milton  阅读(411)  评论(0编辑  收藏  举报

导航