在项目里用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;