- 浏览: 727961 次
- 性别:
- 来自: 天津
文章分类
- 全部博客 (442)
- 中间件 (20)
- hibernate (13)
- spring (20)
- 数据库 (78)
- struts (8)
- ibatis (4)
- 前端 (61)
- linux,windows (21)
- it大环境 (32)
- IDE工具 (36)
- 感悟 (6)
- java基础 (40)
- 经典面试题 (10)
- exception总结 (14)
- 软件设计 (8)
- 工具类应用及新技术 (48)
- php (2)
- 微信 (1)
- 设计模式 (2)
- 重构 (3)
- 管理 (2)
- 工作笔记 (1)
- jmx (1)
- 算法 (4)
- 多线程同步 (2)
- 代码管理工具 (5)
- 代码检测及测试 (2)
- 缓存服务 (1)
- SOA及ROA (5)
- groovy (1)
- 网络编程 (2)
- 大数据 (6)
最新评论
-
love398146779:
我当然不能全写上面了,这只是其中一部分https连接。
java 建立 https连接 -
yuenkin:
大哥,这是双向认证吗?
java 建立 https连接 -
issu:
例如以下代码能遍历字符串"Tom:M ...
<c:forTokens>标签delims截取字符 -
love398146779:
2*3*5=30,是30个以上的请求才拒绝呀。
tomcat的maxThreads、acceptCount(最大线程数、最大排队数) -
love398146779:
2台跟1台一样的效果。
zookeeper与activemq最新存储replicatedLevelDB整合
http://www.ibm.com/developerworks/cn/opensource/os-cn-jcaptcha/
当前越来越多的网站系统采用 CAPTCHA 验证码,来阻止垃圾信息发布机器人的信息提交,但通常绝大多数网站,只提供图片验证码,而这将影响盲人用户的使用。JCaptcha 是一个 Java 开源项目,利用 JCaptcha,不但可以生成图形验证码,还可以利用与 FreeTTS 的集成,来生成声音验证码,而盲人则可以通过识别声音验证码,来正常登录和使用网站的服务。本文将基于四个示例,首先介绍如何使用 JCaptcha 生成图形验证码,最后还介绍了如何开发生成声音验证码。
JCaptcha 简介
CAPTCHA 全称 Completely Automated Public Turing Test to Tell Computers and Humans Apart,最早作为卡内基梅隆大学的一个科研项目,用于生成一个人类容易通过而计算机难以通过的测试,目前广泛应用于网络应用,用于阻止机器人发布垃圾信息。JCaptcha 即为 Java 版本的 CAPTCHA 项目,其是一个开源项目,支持生成图形和声音版的验证码,在生成声音版的验证码时,需要使用到 FreeTTS。目前,JCaptcha 官方网站显示有 2.0 版本,但二进制版只有 1.0 版可供下载,本文亦是基于 1.0 版本展开。
--------------------------------------------------------------------------------
回页首
一个简单的图形验证码
JCaptcha 提供了一定的可扩展能力,用于开发人员创建出复杂的图形验证码。下面,首先利用 JCaptcha 提供的 API 来快速开发一个简单示例。本文的示例为 Web 应用,可以运行在 Tomcat 和 WebSphere 上,除了准备 Web 服务器外,我们还需要准备好 JCaptcha 运行时所必须的 Jar 包。
准备所需的 Jar 包
JCaptcha 项目在实现中,还引用了 commons-collections 和 commons-logging 两个开源项目,再加上 JCaptcha 本身的实现,我们共需要三个包,具体信息如下:
jcaptcha-1.0-all.jarcommons-logging-1.1.1.jarcommons-collections-3.2.jar创建生成图形验证码的 Servlet
在示例 1 中,我们采用 Servlet 生成验证码图片,并将这个图片写到 ServletOutputStream 中,同时将 Servlet 的 response 的类型设置为 image/jpeg。在前台页面中,我们使用 img 标签,并将 src 指向这个 Servlet,这样我们就可以在页面中显示这个生成的验证码图片。清单 1 是 Servlet 代码片段。
清单 1. 生成图形验证码代码片段
// set content type as jpeg
httpServletResponse.setHeader("Cache-Control", "no-store");
httpServletResponse.setHeader("Pragma", "no-cache");
httpServletResponse.setDateHeader("Expires", 0);
httpServletResponse.setContentType("image/jpeg");
// create the image using session ID
logger.fine("tring to get image captcha service");
BufferedImage bufferedImage = service
.getImageChallengeForID(httpServletRequest.getSession(true)
.getId());
ServletOutputStream servletOutputStream = httpServletResponse
.getOutputStream();
// write the image to the servlet output stream
logger.fine("tring to output buffered image to servlet output stream");
ImageIO.write(bufferedImage, "jpg", servletOutputStream);
try {
servletOutputStream.flush();
} finally {
servletOutputStream.close();
}清单 1 中的 service 对象,我们采用了一个 JCaptcha 的默认实现类 DefaultManageableImageCaptchaService,在代码中,我们需要通过获取 service 对象,根据 Session ID 来生成验证码图片。除了使用该默认实现类外,我们也可以通过实现 ImageCaptchaService 接口来生成更为复杂的验证码图片(参见示例 2)。清单 2 为获取 service 对象的示例代码,JCaptcha 建议使用单例来生成 service 对象。
清单 2. 生成 ImageCaptchaService 对象
public class SampleImageCaptchaService {
private static ImageCaptchaService instance;
/**
* Get default manageable image captcha service
* @return ImageCaptchaService
*/
public static ImageCaptchaService getInstance() {
if (instance == null) {
instance = new DefaultManageableImageCaptchaService();
}
return instance;
}
}示例 1 提供了 2 个简单的页面,一个用于显示图形验证码,另一个则在验证成功后显示成功信息。清单 3 为用于显示图形验证码的页面代码片段。
清单 3. 显示图形验证码页面代码片段
<form action="CaptchaValidationServlet" method="post">
<table>
<tr>
<td colspan="2"><img src="ImageCaptchaServlet" /></td>
</tr>
<tr>
<td> 请输入您所看到的字符 :</td>
<td><input type="text" name="captcha_input" value="" />
<%=request.getAttribute("ERROR") == null ? "" :
request.getAttribute("ERROR")%></td>
</tr>
<tr>
<td><input type="submit" value="提交" /></td>
</tr>
</table>
</form>除了实现图形验证码的生成和展示,我们还需要一个 Servlet 来验证用户的输入。用户输入验证的代码的核心,是使用在生成图片时所使用的 service 对象,调用 JCaptcha 的输入验证方法(本示例为 validateResponseForID 方法),来判断用户输入是否正确,清单 4 为输入验证代码片段。
清单 4. 验证用户输入
validated = service.validateResponseForID(
request.getSession(true).getId(), userCaptchaResponse).booleanValue();完成上述代码工作后,我们还需要在 Web.xml 中设置好上面创建的两个 Servlet,然后再部署到 Web 服务器上,图 1 是部署成功后的示例 1 首页。
图 1. 示例 1 的首页
图 1. 示例 1 的首页
Sample1Index.jpg
至此,我们完成了第一个示例,该示例采用了 JCaptcha 提供的默认实现,下面我们将通过扩展使用 JCaptcha,来生成一个更为复杂的图形验证码。
--------------------------------------------------------------------------------
回页首
使用 JCaptcha 生成复杂图形验证码
在实际项目中,我们可能需要使用更为复杂的图形验证码,比如,加入带干扰的图形背景、更多的干扰点、自定义随机字库及图形字体等等,下面将基于示例 2 介绍如何使用 JCaptcha 生成更为复杂的图形验证码。
自定义 ImageCaptchaService 的实现
正如介绍示例 1 时所提到的,我们可以通过实现 ImageCaptchaService 接口来创建我们自己所需要的 service 对象,清单 5 为示例 2 对接口 ImageCaptchaService 的实现,我们可以在该实现中,使用自己开发的图形验证码生成引擎(SampleListImageCaptchaEngine)来生成更为复杂的图形验证码。
清单 5. 示例 2 中 ImageCaptchaService 的实现
public class SampleImageCaptchaService extends
AbstractManageableImageCaptchaService implements ImageCaptchaService {
private static SampleImageCaptchaService instance;
public static SampleImageCaptchaService getInstance() {
if (instance == null) {
//use customized engine
ListImageCaptchaEngine engine = new SampleListImageCaptchaEngine();
instance = new SampleImageCaptchaService(
new FastHashMapCaptchaStore(), engine, 180, 100000, 75000);
}
return instance;
}
public SampleImageCaptchaService(CaptchaStore captchaStore,
CaptchaEngine captchaEngine, int minGuarantedStorageDelayInSeconds,
int maxCaptchaStoreSize, int captchaStoreLoadBeforeGarbageCollection) {
super(captchaStore, captchaEngine, minGuarantedStorageDelayInSeconds,
maxCaptchaStoreSize, captchaStoreLoadBeforeGarbageCollection);
}
}构建生成图形验证码的 ImageCaptchaEngine
清单 5 中的 FastHashMapCaptchaStore 用于存放生成的验证码字符,FastHashMapCaptchaStore 基本可以满足绝大多数项目需求,我们通常不需要创建自己的类来实现它的功能;SampleListImageCaptchaEngine 则是扩展的核心,该类是 ImageCaptchaEngine 的一个实现,在该类中,我们可以设置随机字库、生成的字体和大小、字符数、背景图片以及干扰点等等。清单 6 为 SampleListImageCaptchaEngine 代码片段。
清单 6. SampleListImageCaptchaEngine 代码片段
//create text parser
TextPaster randomPaster = new DecoratedRandomTextPaster(new Integer(8),
new Integer(10), new SingleColorGenerator(Color.BLACK),
new TextDecorator[] { new BaffleTextDecorator(new Integer(1), Color.WHITE) });
//create image captcha factory
ImageCaptchaFactory factory = new GimpyFactory(
new RandomWordGenerator("abcdefghijklmnopqrstuvwxyz"),
new ComposedWordToImage(new TwistedRandomFontGenerator(new Integer(34),
new Integer(40)), new FunkyBackgroundGenerator(new Integer(
260), new Integer(70)), randomPaster));
ImageCaptchaFactory characterFactory[] = { factory};
this.addFactories(characterFactory);在清单 6 中,DecoratedRandomTextPaster 的第一个参数用于设置验证码最少字符数,第二个参数为最多的字符数,第三个参数 SingleColorGenerator 为字体颜色,这里为黑色,TextDecorator 为干扰设置,这里是一个字符一个干扰点,并且干扰点为白色。
在 ImageCaptchaFactory 中,第一个参数设置了随机字库,这里为英文字母,在第二个参数中,TwistedRandomFontGenerator 设置了生成的字符字体,最小 34,最大为 40,FunkyBackgroundGenerator 则用于生成干扰背景,除了设置字体大小外,还需要设置生成的图片大小,示例 2 为 260*70 像素。图 2 为示例 2 的首页截图。
图 2. 示例 2 的首页
图 2. 示例 2 的首页
Sample2Index.jpg
可以说 ImageCaptchaEngine 的不同实现,决定了图形验证码的不同样式,JCaptcha 目前提供了多种 ImageCaptchaEngine 的实现,这些实现提供了多种不同的图形验证码样式,当然,我们也可以自己实现 TextPaster 及 TextDecorator 等,进而再继承实现 ImageCaptchaEngine,从而实现自己所期望的效果。图 3 为 JCaptcha 官方网站提供的部分 ImageCaptchaEngine 的实现截图,更多样式,请参见 官方网站。
图 3. JCaptcha 官方图形验证码示例
图 3. JCaptcha 官方图形验证码示例
JCaptchaSample.jpg
--------------------------------------------------------------------------------
回页首
开发声音验证码
由于某些项目需要支持盲人使用,而盲人无法看到图形验证码,这时,我们就需要同时提供声音版的验证码。JCaptcha 同时支持声音版验证码,但默认实现并不支持生成的声音和图形验证码中的字符一致,这里就需要通过一定的扩展定制,才能保证声音和图形验证码的内容一致,下面首先介绍如何生成声音验证码,然后介绍如何扩展定制,才能保证声音和图形验证码的内容的一致。
FreeTTS
JCaptcha 使用了 FreeTTS 来开发声音验证码,FreeTTS 是一个采用 Java 编写的语音合成项目,它基于 Flite 项目编写,Flite 项目是一个由卡内基梅隆大学开发的小型语音合成引擎,其又源于爱丁堡大学的 Festival 语音合成系统和卡内基梅隆大学的 FestVox 项目。本文的示例使用了 FreeTTS 1.2.2,该版本的有如下几个方面的功能:
一个语音合成引擎支持多种声音
1 个 8khz,多音位,男性美国英语发音1 个 16khz,多音位,男性美国英语发音1 个 16khz 有限声音域的男性美国英语发音支持从 FestVox 导入声音(仅支持美国英语)一定程度上支持从 FestVox 中导入 CMU ARCTIC 声音支持 MBROLA 声音(需另外下载该功能包)
1 个 16khz 女性美国英语发音2 个 16khz 男性美国英语发音部分支持 JSAPI 1.0为了使用 FreeTTS,JCaptcha 还另外提供了一个 Jar 包用于和 FreeTTS 的集成,所以在运行过程中,除了需要引入 FreeTTS 自带的 Jar 包,还需要包括 JCaptcha 提供的和 FreeTTS 集成的包,在示例 3 和 4 中,使用了如下的包:
jcaptcha-extension-sound-freetts-1.0.jar:用于和 FreeTTS 的集成jcaptcha-1.0-all.jar: JCaptcha 核心包freetts.jar:FreeTTS 核心包en_us.jar:用于 FreeTTScommons-logging-1.1.1.jar:用于 JCaptchacommons-collections-3.2.jar:用于 JCaptchacmutimelex.jar:用于 FreeTTScmulex.jar:用于 FreeTTScmudict04.jar:用于 FreeTTScmu_us_kal.jar:用于 FreeTTScmu_time_awb.jar:用于 FreeTTS构建生成声音的 SoundCaptchaEngine
同 ImageCaptchaEngine 一样,JCaptcha 同时提供了 SoundCaptchaEngine,所以整个声音验证码的核心是创建 SoundCaptchaEngine 的实现,当然 JCaptcha 也提供了一些默认的实现,比如 DefaultSoundCaptchaEngine 以及 SpellerSoundCaptchaEngine 等等,为了考虑能够支持生成和图形验证码相同的字符,示例 3 采用继承抽象类 ListSoundCaptchaEngine 的方式,来创建自己的实现 SampleListSoundCaptchaEngine。清单 7 为 SampleListSoundCaptchaEngine 代码片段。
清单 7. SampleListSoundCaptchaEngine 代码片段
public class SampleListSoundCaptchaEngine extends ListSoundCaptchaEngine {
private static String voiceName = "kevin16";
private static String voicePackage =
"com.sun.speech.freetts.en.us.cmu_us_kal.KevinVoiceDirectory";
protected void buildInitialFactories() {
//create word generator
WordGenerator wordGenerator = new RandomWordGenerator("ABCDEFGHIJKLMNOPQRSTUVWXYZ");
//create decorator
SpellerWordDecorator decorator = new SpellerWordDecorator(", ");
//create sound factory
SoundCaptchaFactory soundFactory[] = { new SpellerSoundFactory(
wordGenerator,
new FreeTTSWordToSound(new FreeTTSSoundConfigurator(voiceName,
voicePackage, 1.0f, 100, 100), 4, 10), decorator) };
this.setFactories(soundFactory);
}
}创建生成声音验证码的 Servlet
同开发图形验证码一样,这里也需要创建 SoundCaptchaService 的实现,实现方式与 ImageCaptchaService 的实现类似,示例 3 的实现类为 SampleSoundCaptchaService,这里不做叙述,读者可以参见附带的示例源代码。
和 ImageCaptchaService 的实现不同的是,这里不能采用单例的方式来获取 SoundCaptchaService 的实现,否则不能多次为同一 Session ID 生成多个声音验证码文件。另外,在用户不刷新页面,而重复点击声音验证码图标时,我们需要提供同一段声音,因此,我们可以将 SoundCaptchaService 的实现对象以及所生成的字节码流放入到 session 中,清单 7 为 SoundCaptchaServlet 代码片段。
清单 8. SoundCaptchaServlet 代码片段
httpServletResponse.setContentType("audio/x-wav");
SoundCaptchaService service = null;
if ( httpServletRequest.getSession().getAttribute("soundService") != null) {
service = (SoundCaptchaService) httpServletRequest
.getSession().getAttribute("soundService");
} else {
service = new SampleSoundCaptchaService();
}
// get AudioInputStream using session
ByteArrayOutputStream byteOutputStream = new ByteArrayOutputStream();
//get from the session if already existed, otherwise, create new one
if (httpServletRequest.getSession().getAttribute("stream") == null) {
AudioInputStream audioInputStream = service.getSoundChallengeForID(
httpServletRequest.getSession().getId(), httpServletRequest.getLocale());
AudioSystem.write(audioInputStream, javax.sound.sampled.AudioFileFormat.Type.WAVE,
byteOutputStream);
} else {
byteOutputStream =
(ByteArrayOutputStream)httpServletRequest.getSession().getAttribute("stream");
}
// output to servlet
ServletOutputStream servletOutputStream = httpServletResponse.getOutputStream();
servletOutputStream.write(byteOutputStream.toByteArray());
// save the service object to session, will use it for validation
// purpose
httpServletRequest.getSession().setAttribute("soundService", service);
httpServletRequest.getSession().setAttribute("stream", byteOutputStream);
// output to servlet response stream
try {
servletOutputStream.flush();
} finally {
servletOutputStream.close();
}创建 Web 页面和验证代码
为了方便用户使用,我们需要在页面上放置一个图标,当焦点落在图标上(考虑到盲人可能使用键盘 Tab 键来切换焦点),触发 onFocus 事件来调用 JavaScript 方法显示 embed 标签,从而调用上面生成声音验证码的 Servlet。图 4 为示例 3 首页截图。
图 4. 示例 3 运行页面截图
图 4. 示例 3 运行页面截图
Sample3Index.jpg
当声音验证码图标在已经获取焦点,并朗读后,用户可能多次重复让该图标获取焦点以便多次收听,这时,JavaScript 方法会被多次触发,如果不做处理,其将多次请求 Servlet,以致生成不同的声音文件,本示例采用了前一个小节所介绍的缓存 SoundCaptchaService 对象和字节码流的方法来解决这个问题,以保证多次能收听到同一段声音。当然,也可以采用在 URL 后面加一个随机 token 的方式,来判断是否重复提交。清单 8 为示例 3 首页代码片段。
清单 9. 示例 3 index.jsp 代码片段
<%
request.getSession().removeAttribute("soundService");
request.getSession().removeAttribute("stream");
%>
<html>
<head>
<script type="text/javascript">
function playSoundCaptcha() {
var wavURL = '<%= request.getContextPath() %>'+'/SoundCaptchaServlet';
var embedAudioPlayer = "<EMBED SRC='" + wavURL + "' HIDDEN='true' AUTOSTART='true' />";
var wavArea = document.getElementById("wavArea");
wavArea.innerHTML = embedAudioPlayer;
}
</script>
</head>
<form action="CaptchaValidationServlet" method="post">
<table>
<tr>
<td colspan="2"><img src="ImageCaptchaServlet" /></td>
</tr>
<tr>
<td> 请输入您所看到的字符 :</td>
<td><input type="text" name="captcha_input" value="" />
<a href="#" onFocus="playSoundCaptcha()">
<img src="image/wheelchair.jpg" height="18px" width="18px"
alt=""></a><%=request.getAttribute("ERROR") == null ? "" :
request .getAttribute("ERROR")%></td>
</tr>
<tr>
<td><input type="submit" value="提交" /></td>
</tr>
<div id="wavArea" style="display:none">
</div>
</table>
</form>可以看到,我们采用了不在页面显示的 embed 标签来调用 Servlet 以获取声音流,然后自动播放。在每次页面刷新的时候,还需要将 Session 中的 SoundCaptchaService 的实现对象以及音频流删除,以便页面刷新时,重新生成新的声音验证码。
后台用来验证用户输入的逻辑和图形版的验证方式相似,这里不做介绍,具体参见示例源代码。需要注意的是,由于示例 3 生成的声音验证码中的字符和图形版的字符并不一致,在后台验证的逻辑中,只需要用户输入等于声音验证码或图形验证码的字符,即可认为用户输入正确。下面将介绍如何才能使声音版和图形版中的字符保持一致。
--------------------------------------------------------------------------------
回页首
使声音验证码和图形验证码字符一致
JCaptcha 并没有提供很直观的方式,来保证声音验证码和图形验证码中的字符一致,这就需要我们了解 JCaptcha 的实现,并能够通过继承的方式来扩展定制 JCaptcha,从而实现声音和图形验证码字符的一致。
由于图形验证码会先于声音验证码生成,所以,我们第一步就是需要获取图形验证码所生成的字符串,然后是利用所获取的字符串来生成声音验证码。
获取图形验证码随机数
在以上示例中,虽然我们在实现 ImageCaptchaEngine 和 SoundCaptchaEngine 的时候,可以设置随机数生成类,比如 RandomWordGenerator 等,但是即使声音版和图形版采用同一个随机数生成对象,也不能保证会生成同一个字符,因为它们仅仅是设定一个随机字库,而字符则是每次随机生成。因而,我们并不能通过使用同样的随机数生成对象来生成同样的随机数,只能通过考虑使用图形版生成的字符来生成声音验证码,才能保持两者的一致。
在创建 ImageCaptchaEngine 的实现时,我们需要提供一个 ImageCaptchaFactory,实际上,我们可以通过使用继承实现 JCaptcha 已有 ImageCaptchaFactory 的实现,来获取生成的随机数。清单 9 是示例 4 中继承了 GimpyFactory 的代码片段。
清单 9. SampleGimpyFactory 代码片段
public class SampleGimpyFactory extends GimpyFactory {
……
public ImageCaptcha getImageCaptcha(Locale locale) {
//length
Integer wordLength = getRandomLength();
String word = getWordGenerator().getWord(wordLength, locale);
if (this.wordBridge != null) {
this.wordBridge.setGeneratedWord(word);
}
BufferedImage image = null;
try {
image = getWordToImage().getImage(word);
} catch (Throwable e) {
throw new CaptchaException(e);
}
ImageCaptcha captcha =
new SampleGimpy(CaptchaQuestionHelper.getQuestion(locale, BUNDLE_QUESTION_KEY),
image, word);
return captcha;
}
}可以发现,通过使用上面的 SampleGimpyFactory 即可获取图形验证码的随机数,所以我们可以将清单 6 中的 GimpyFactory 替换为 SampleGimpyFactory。
在获取了生成的随机数后,还需考虑如何将该随机数传递给生成声音验证码的代码。考虑到我们在生成验证码时,都是基于同一个 Session ID 生成,那么我们就可以将生成的随机数放到 map 中,而 key 是 Session ID,那么,在生成验证码字符后,我们就可以用 Session ID 取出该字符串,并放到 Session 中,然后,在生成声音验证码的代码中,就可以从 Session 中获取随机数。但是,并不是所有代码都可以很方便的获取到 ID,所以,我们还需要对示例 2 的代码进行改造,以便能够根据 ID 保存随机数。清单 10 为改造后的清单 5 中的类 SampleImageCaptchaService 的代码片段。
清单 10. SampleImageCaptchaService 代码片段
public class SampleImageCaptchaService extends
AbstractManageableImageCaptchaService implements ImageCaptchaService {
… ..
@Override
public BufferedImage getImageChallengeForID(String ID)
throws CaptchaServiceException {
BufferedImage image= super.getImageChallengeForID(ID);
String generatedWord = ((SampleListImageCaptchaEngine) engine).getWordBridge()
.getGeneratedWord();
WordMap.getWordsMap().put(ID, generatedWord);
return image;
}
}如清单 10 所示,我们将生成的随机数放到了一个 map 中,而 key 则是 ID,这里也就是 SessionID,然后我们就可以在 ImageCaptchaServlet 获取并将该随机数放到 Session 中,如清单 11 所示。接下来便是如何利用该字符生成声音验证码。
清单 11. ImageCaptchaServlet 代码片段
httpServletRequest.getSession().setAttribute(
"generatedWord",
WordMap.getWordsMap().get(httpServletRequest.getSession(true)
.getId()));利用指定字符生成声音验证码
为了能够使用指定的字符串生成验证码,我们需要对示例 3 中的代码做一定的修改。这里,示例 4 通过继承 SpellerSoundFactory 实现了一个扩展的 SoundCaptchaFactory,清单 12 为代码片段。
清单 12. SampleSpellerSoundFactory 代码片段
public class SampleSpellerSoundFactory extends SpellerSoundFactory {
private String word;
……
@Override
public SoundCaptcha getSoundCaptcha() {
return getSoundCaptcha(Locale.getDefault());
}
@Override
public SoundCaptcha getSoundCaptcha(Locale locale) {
String soundWord = "";
if (this.word != null && !this.word.equals("")) {
soundWord = this.word;
} else {
soundWord = this.wordGenerator.getWord(getRandomLength(), locale);
}
AudioInputStream sound = this.word2Sound.getSound(wordDecorator
.decorateWord(soundWord), locale);
SoundCaptcha soundCaptcha = new SpellerSound(getQuestion(locale),
sound, word);
return soundCaptcha;
}
}根据清单 11 中的代码,如果字符串能够正确的传进来,这个 SampleSpellerSoundFactory 将可以根据该字符串生成声音验证码。
相应的,清单 7 中的 SampleListSoundCaptchaEngine 需要做如清单 13 所示的修改。
清单 13. SampleListSoundCaptchaEngine 代码
public class SampleListSoundCaptchaEngine extends ListSoundCaptchaEngine {
private String word;
private static String voiceName = "kevin16";
private static String voicePackage =
"com.sun.speech.freetts.en.us.cmu_us_kal.KevinVoiceDirectory";
protected void buildInitialFactories() {
WordGenerator wordGenerator = new RandomWordGenerator("ABCDEFGHIJKLMNOPQRSTUVWXYZ");
SpellerWordDecorator decorator = new SpellerWordDecorator(", ");
SoundCaptchaFactory soundFactory[] = { new SampleSpellerSoundFactory(
wordGenerator,
new FreeTTSWordToSound(new FreeTTSSoundConfigurator(voiceName,
voicePackage, 1.0f, 100, 100), 4, 10), decorator, word) };
this.setFactories(soundFactory);
}
public SampleListSoundCaptchaEngine(String word) {
this.word = word;
buildInitialFactories();
}
}需要注意的是,我们一定要有如清单 13 所示的带参数的够找函数,否则初始化时,该类会首先调用父类的构造函数,其父类会直接调用 buildInitialFactories 函数,而此时字符串还没有传递给父类。
接下来需要修改 SampleSoundCaptchaService,在该类中使用 SampleListSoundCaptchaEngine 并传入随机数参数。
清单 14. SampleSoundCaptchaService 代码片段
public class SampleSoundCaptchaService extends
AbstractManageableSoundCaptchaService implements SoundCaptchaService {
public SampleSoundCaptchaService(String word) {
super(new FastHashMapCaptchaStore(),
new SampleListSoundCaptchaEngine(word), 180, 100000, 75000);
}
……
}最后,我们只需要修改 SoundCaptchaServlet,先依据 Session ID 获取生成的随机数,然后调用清单 14 的 SampleSoundCaptchaService 生成声音验证码,如清单 15 所示。
清单 15. SoundCaptchaServlet 代码片段
String word = "";
if (httpServletRequest .getSession().getAttribute("generatedWord") != null) {
word = (String)httpServletRequest.getSession().getAttribute("generatedWord");
logger.info("Get generated word from the session, word=" + word);
}
service = new SampleSoundCaptchaService(word);至于后台验证逻辑,可以不做修改,也可以删除原先用于验证声音验证码的代码。至此,声音验证码的字符将与图形验证码的字符保持一致。
--------------------------------------------------------------------------------
回页首
总结
本文先从一个简单的示例入手,介绍了如何采用 JCaptcha 开发一个简单的图形验证码,然后介绍了如何通过扩展定制,实现更为复杂的图形验证码,接下来介绍了如何采用 JCaptcha 开发声音验证码,并在最后,介绍了如何扩展 JCaptcha 来保证图形验证码和声音验证码的内容一致。
当前越来越多的网站系统采用 CAPTCHA 验证码,来阻止垃圾信息发布机器人的信息提交,但通常绝大多数网站,只提供图片验证码,而这将影响盲人用户的使用。JCaptcha 是一个 Java 开源项目,利用 JCaptcha,不但可以生成图形验证码,还可以利用与 FreeTTS 的集成,来生成声音验证码,而盲人则可以通过识别声音验证码,来正常登录和使用网站的服务。本文将基于四个示例,首先介绍如何使用 JCaptcha 生成图形验证码,最后还介绍了如何开发生成声音验证码。
JCaptcha 简介
CAPTCHA 全称 Completely Automated Public Turing Test to Tell Computers and Humans Apart,最早作为卡内基梅隆大学的一个科研项目,用于生成一个人类容易通过而计算机难以通过的测试,目前广泛应用于网络应用,用于阻止机器人发布垃圾信息。JCaptcha 即为 Java 版本的 CAPTCHA 项目,其是一个开源项目,支持生成图形和声音版的验证码,在生成声音版的验证码时,需要使用到 FreeTTS。目前,JCaptcha 官方网站显示有 2.0 版本,但二进制版只有 1.0 版可供下载,本文亦是基于 1.0 版本展开。
--------------------------------------------------------------------------------
回页首
一个简单的图形验证码
JCaptcha 提供了一定的可扩展能力,用于开发人员创建出复杂的图形验证码。下面,首先利用 JCaptcha 提供的 API 来快速开发一个简单示例。本文的示例为 Web 应用,可以运行在 Tomcat 和 WebSphere 上,除了准备 Web 服务器外,我们还需要准备好 JCaptcha 运行时所必须的 Jar 包。
准备所需的 Jar 包
JCaptcha 项目在实现中,还引用了 commons-collections 和 commons-logging 两个开源项目,再加上 JCaptcha 本身的实现,我们共需要三个包,具体信息如下:
jcaptcha-1.0-all.jarcommons-logging-1.1.1.jarcommons-collections-3.2.jar创建生成图形验证码的 Servlet
在示例 1 中,我们采用 Servlet 生成验证码图片,并将这个图片写到 ServletOutputStream 中,同时将 Servlet 的 response 的类型设置为 image/jpeg。在前台页面中,我们使用 img 标签,并将 src 指向这个 Servlet,这样我们就可以在页面中显示这个生成的验证码图片。清单 1 是 Servlet 代码片段。
清单 1. 生成图形验证码代码片段
// set content type as jpeg
httpServletResponse.setHeader("Cache-Control", "no-store");
httpServletResponse.setHeader("Pragma", "no-cache");
httpServletResponse.setDateHeader("Expires", 0);
httpServletResponse.setContentType("image/jpeg");
// create the image using session ID
logger.fine("tring to get image captcha service");
BufferedImage bufferedImage = service
.getImageChallengeForID(httpServletRequest.getSession(true)
.getId());
ServletOutputStream servletOutputStream = httpServletResponse
.getOutputStream();
// write the image to the servlet output stream
logger.fine("tring to output buffered image to servlet output stream");
ImageIO.write(bufferedImage, "jpg", servletOutputStream);
try {
servletOutputStream.flush();
} finally {
servletOutputStream.close();
}清单 1 中的 service 对象,我们采用了一个 JCaptcha 的默认实现类 DefaultManageableImageCaptchaService,在代码中,我们需要通过获取 service 对象,根据 Session ID 来生成验证码图片。除了使用该默认实现类外,我们也可以通过实现 ImageCaptchaService 接口来生成更为复杂的验证码图片(参见示例 2)。清单 2 为获取 service 对象的示例代码,JCaptcha 建议使用单例来生成 service 对象。
清单 2. 生成 ImageCaptchaService 对象
public class SampleImageCaptchaService {
private static ImageCaptchaService instance;
/**
* Get default manageable image captcha service
* @return ImageCaptchaService
*/
public static ImageCaptchaService getInstance() {
if (instance == null) {
instance = new DefaultManageableImageCaptchaService();
}
return instance;
}
}示例 1 提供了 2 个简单的页面,一个用于显示图形验证码,另一个则在验证成功后显示成功信息。清单 3 为用于显示图形验证码的页面代码片段。
清单 3. 显示图形验证码页面代码片段
<form action="CaptchaValidationServlet" method="post">
<table>
<tr>
<td colspan="2"><img src="ImageCaptchaServlet" /></td>
</tr>
<tr>
<td> 请输入您所看到的字符 :</td>
<td><input type="text" name="captcha_input" value="" />
<%=request.getAttribute("ERROR") == null ? "" :
request.getAttribute("ERROR")%></td>
</tr>
<tr>
<td><input type="submit" value="提交" /></td>
</tr>
</table>
</form>除了实现图形验证码的生成和展示,我们还需要一个 Servlet 来验证用户的输入。用户输入验证的代码的核心,是使用在生成图片时所使用的 service 对象,调用 JCaptcha 的输入验证方法(本示例为 validateResponseForID 方法),来判断用户输入是否正确,清单 4 为输入验证代码片段。
清单 4. 验证用户输入
validated = service.validateResponseForID(
request.getSession(true).getId(), userCaptchaResponse).booleanValue();完成上述代码工作后,我们还需要在 Web.xml 中设置好上面创建的两个 Servlet,然后再部署到 Web 服务器上,图 1 是部署成功后的示例 1 首页。
图 1. 示例 1 的首页
图 1. 示例 1 的首页
Sample1Index.jpg
至此,我们完成了第一个示例,该示例采用了 JCaptcha 提供的默认实现,下面我们将通过扩展使用 JCaptcha,来生成一个更为复杂的图形验证码。
--------------------------------------------------------------------------------
回页首
使用 JCaptcha 生成复杂图形验证码
在实际项目中,我们可能需要使用更为复杂的图形验证码,比如,加入带干扰的图形背景、更多的干扰点、自定义随机字库及图形字体等等,下面将基于示例 2 介绍如何使用 JCaptcha 生成更为复杂的图形验证码。
自定义 ImageCaptchaService 的实现
正如介绍示例 1 时所提到的,我们可以通过实现 ImageCaptchaService 接口来创建我们自己所需要的 service 对象,清单 5 为示例 2 对接口 ImageCaptchaService 的实现,我们可以在该实现中,使用自己开发的图形验证码生成引擎(SampleListImageCaptchaEngine)来生成更为复杂的图形验证码。
清单 5. 示例 2 中 ImageCaptchaService 的实现
public class SampleImageCaptchaService extends
AbstractManageableImageCaptchaService implements ImageCaptchaService {
private static SampleImageCaptchaService instance;
public static SampleImageCaptchaService getInstance() {
if (instance == null) {
//use customized engine
ListImageCaptchaEngine engine = new SampleListImageCaptchaEngine();
instance = new SampleImageCaptchaService(
new FastHashMapCaptchaStore(), engine, 180, 100000, 75000);
}
return instance;
}
public SampleImageCaptchaService(CaptchaStore captchaStore,
CaptchaEngine captchaEngine, int minGuarantedStorageDelayInSeconds,
int maxCaptchaStoreSize, int captchaStoreLoadBeforeGarbageCollection) {
super(captchaStore, captchaEngine, minGuarantedStorageDelayInSeconds,
maxCaptchaStoreSize, captchaStoreLoadBeforeGarbageCollection);
}
}构建生成图形验证码的 ImageCaptchaEngine
清单 5 中的 FastHashMapCaptchaStore 用于存放生成的验证码字符,FastHashMapCaptchaStore 基本可以满足绝大多数项目需求,我们通常不需要创建自己的类来实现它的功能;SampleListImageCaptchaEngine 则是扩展的核心,该类是 ImageCaptchaEngine 的一个实现,在该类中,我们可以设置随机字库、生成的字体和大小、字符数、背景图片以及干扰点等等。清单 6 为 SampleListImageCaptchaEngine 代码片段。
清单 6. SampleListImageCaptchaEngine 代码片段
//create text parser
TextPaster randomPaster = new DecoratedRandomTextPaster(new Integer(8),
new Integer(10), new SingleColorGenerator(Color.BLACK),
new TextDecorator[] { new BaffleTextDecorator(new Integer(1), Color.WHITE) });
//create image captcha factory
ImageCaptchaFactory factory = new GimpyFactory(
new RandomWordGenerator("abcdefghijklmnopqrstuvwxyz"),
new ComposedWordToImage(new TwistedRandomFontGenerator(new Integer(34),
new Integer(40)), new FunkyBackgroundGenerator(new Integer(
260), new Integer(70)), randomPaster));
ImageCaptchaFactory characterFactory[] = { factory};
this.addFactories(characterFactory);在清单 6 中,DecoratedRandomTextPaster 的第一个参数用于设置验证码最少字符数,第二个参数为最多的字符数,第三个参数 SingleColorGenerator 为字体颜色,这里为黑色,TextDecorator 为干扰设置,这里是一个字符一个干扰点,并且干扰点为白色。
在 ImageCaptchaFactory 中,第一个参数设置了随机字库,这里为英文字母,在第二个参数中,TwistedRandomFontGenerator 设置了生成的字符字体,最小 34,最大为 40,FunkyBackgroundGenerator 则用于生成干扰背景,除了设置字体大小外,还需要设置生成的图片大小,示例 2 为 260*70 像素。图 2 为示例 2 的首页截图。
图 2. 示例 2 的首页
图 2. 示例 2 的首页
Sample2Index.jpg
可以说 ImageCaptchaEngine 的不同实现,决定了图形验证码的不同样式,JCaptcha 目前提供了多种 ImageCaptchaEngine 的实现,这些实现提供了多种不同的图形验证码样式,当然,我们也可以自己实现 TextPaster 及 TextDecorator 等,进而再继承实现 ImageCaptchaEngine,从而实现自己所期望的效果。图 3 为 JCaptcha 官方网站提供的部分 ImageCaptchaEngine 的实现截图,更多样式,请参见 官方网站。
图 3. JCaptcha 官方图形验证码示例
图 3. JCaptcha 官方图形验证码示例
JCaptchaSample.jpg
--------------------------------------------------------------------------------
回页首
开发声音验证码
由于某些项目需要支持盲人使用,而盲人无法看到图形验证码,这时,我们就需要同时提供声音版的验证码。JCaptcha 同时支持声音版验证码,但默认实现并不支持生成的声音和图形验证码中的字符一致,这里就需要通过一定的扩展定制,才能保证声音和图形验证码的内容一致,下面首先介绍如何生成声音验证码,然后介绍如何扩展定制,才能保证声音和图形验证码的内容的一致。
FreeTTS
JCaptcha 使用了 FreeTTS 来开发声音验证码,FreeTTS 是一个采用 Java 编写的语音合成项目,它基于 Flite 项目编写,Flite 项目是一个由卡内基梅隆大学开发的小型语音合成引擎,其又源于爱丁堡大学的 Festival 语音合成系统和卡内基梅隆大学的 FestVox 项目。本文的示例使用了 FreeTTS 1.2.2,该版本的有如下几个方面的功能:
一个语音合成引擎支持多种声音
1 个 8khz,多音位,男性美国英语发音1 个 16khz,多音位,男性美国英语发音1 个 16khz 有限声音域的男性美国英语发音支持从 FestVox 导入声音(仅支持美国英语)一定程度上支持从 FestVox 中导入 CMU ARCTIC 声音支持 MBROLA 声音(需另外下载该功能包)
1 个 16khz 女性美国英语发音2 个 16khz 男性美国英语发音部分支持 JSAPI 1.0为了使用 FreeTTS,JCaptcha 还另外提供了一个 Jar 包用于和 FreeTTS 的集成,所以在运行过程中,除了需要引入 FreeTTS 自带的 Jar 包,还需要包括 JCaptcha 提供的和 FreeTTS 集成的包,在示例 3 和 4 中,使用了如下的包:
jcaptcha-extension-sound-freetts-1.0.jar:用于和 FreeTTS 的集成jcaptcha-1.0-all.jar: JCaptcha 核心包freetts.jar:FreeTTS 核心包en_us.jar:用于 FreeTTScommons-logging-1.1.1.jar:用于 JCaptchacommons-collections-3.2.jar:用于 JCaptchacmutimelex.jar:用于 FreeTTScmulex.jar:用于 FreeTTScmudict04.jar:用于 FreeTTScmu_us_kal.jar:用于 FreeTTScmu_time_awb.jar:用于 FreeTTS构建生成声音的 SoundCaptchaEngine
同 ImageCaptchaEngine 一样,JCaptcha 同时提供了 SoundCaptchaEngine,所以整个声音验证码的核心是创建 SoundCaptchaEngine 的实现,当然 JCaptcha 也提供了一些默认的实现,比如 DefaultSoundCaptchaEngine 以及 SpellerSoundCaptchaEngine 等等,为了考虑能够支持生成和图形验证码相同的字符,示例 3 采用继承抽象类 ListSoundCaptchaEngine 的方式,来创建自己的实现 SampleListSoundCaptchaEngine。清单 7 为 SampleListSoundCaptchaEngine 代码片段。
清单 7. SampleListSoundCaptchaEngine 代码片段
public class SampleListSoundCaptchaEngine extends ListSoundCaptchaEngine {
private static String voiceName = "kevin16";
private static String voicePackage =
"com.sun.speech.freetts.en.us.cmu_us_kal.KevinVoiceDirectory";
protected void buildInitialFactories() {
//create word generator
WordGenerator wordGenerator = new RandomWordGenerator("ABCDEFGHIJKLMNOPQRSTUVWXYZ");
//create decorator
SpellerWordDecorator decorator = new SpellerWordDecorator(", ");
//create sound factory
SoundCaptchaFactory soundFactory[] = { new SpellerSoundFactory(
wordGenerator,
new FreeTTSWordToSound(new FreeTTSSoundConfigurator(voiceName,
voicePackage, 1.0f, 100, 100), 4, 10), decorator) };
this.setFactories(soundFactory);
}
}创建生成声音验证码的 Servlet
同开发图形验证码一样,这里也需要创建 SoundCaptchaService 的实现,实现方式与 ImageCaptchaService 的实现类似,示例 3 的实现类为 SampleSoundCaptchaService,这里不做叙述,读者可以参见附带的示例源代码。
和 ImageCaptchaService 的实现不同的是,这里不能采用单例的方式来获取 SoundCaptchaService 的实现,否则不能多次为同一 Session ID 生成多个声音验证码文件。另外,在用户不刷新页面,而重复点击声音验证码图标时,我们需要提供同一段声音,因此,我们可以将 SoundCaptchaService 的实现对象以及所生成的字节码流放入到 session 中,清单 7 为 SoundCaptchaServlet 代码片段。
清单 8. SoundCaptchaServlet 代码片段
httpServletResponse.setContentType("audio/x-wav");
SoundCaptchaService service = null;
if ( httpServletRequest.getSession().getAttribute("soundService") != null) {
service = (SoundCaptchaService) httpServletRequest
.getSession().getAttribute("soundService");
} else {
service = new SampleSoundCaptchaService();
}
// get AudioInputStream using session
ByteArrayOutputStream byteOutputStream = new ByteArrayOutputStream();
//get from the session if already existed, otherwise, create new one
if (httpServletRequest.getSession().getAttribute("stream") == null) {
AudioInputStream audioInputStream = service.getSoundChallengeForID(
httpServletRequest.getSession().getId(), httpServletRequest.getLocale());
AudioSystem.write(audioInputStream, javax.sound.sampled.AudioFileFormat.Type.WAVE,
byteOutputStream);
} else {
byteOutputStream =
(ByteArrayOutputStream)httpServletRequest.getSession().getAttribute("stream");
}
// output to servlet
ServletOutputStream servletOutputStream = httpServletResponse.getOutputStream();
servletOutputStream.write(byteOutputStream.toByteArray());
// save the service object to session, will use it for validation
// purpose
httpServletRequest.getSession().setAttribute("soundService", service);
httpServletRequest.getSession().setAttribute("stream", byteOutputStream);
// output to servlet response stream
try {
servletOutputStream.flush();
} finally {
servletOutputStream.close();
}创建 Web 页面和验证代码
为了方便用户使用,我们需要在页面上放置一个图标,当焦点落在图标上(考虑到盲人可能使用键盘 Tab 键来切换焦点),触发 onFocus 事件来调用 JavaScript 方法显示 embed 标签,从而调用上面生成声音验证码的 Servlet。图 4 为示例 3 首页截图。
图 4. 示例 3 运行页面截图
图 4. 示例 3 运行页面截图
Sample3Index.jpg
当声音验证码图标在已经获取焦点,并朗读后,用户可能多次重复让该图标获取焦点以便多次收听,这时,JavaScript 方法会被多次触发,如果不做处理,其将多次请求 Servlet,以致生成不同的声音文件,本示例采用了前一个小节所介绍的缓存 SoundCaptchaService 对象和字节码流的方法来解决这个问题,以保证多次能收听到同一段声音。当然,也可以采用在 URL 后面加一个随机 token 的方式,来判断是否重复提交。清单 8 为示例 3 首页代码片段。
清单 9. 示例 3 index.jsp 代码片段
<%
request.getSession().removeAttribute("soundService");
request.getSession().removeAttribute("stream");
%>
<html>
<head>
<script type="text/javascript">
function playSoundCaptcha() {
var wavURL = '<%= request.getContextPath() %>'+'/SoundCaptchaServlet';
var embedAudioPlayer = "<EMBED SRC='" + wavURL + "' HIDDEN='true' AUTOSTART='true' />";
var wavArea = document.getElementById("wavArea");
wavArea.innerHTML = embedAudioPlayer;
}
</script>
</head>
<form action="CaptchaValidationServlet" method="post">
<table>
<tr>
<td colspan="2"><img src="ImageCaptchaServlet" /></td>
</tr>
<tr>
<td> 请输入您所看到的字符 :</td>
<td><input type="text" name="captcha_input" value="" />
<a href="#" onFocus="playSoundCaptcha()">
<img src="image/wheelchair.jpg" height="18px" width="18px"
alt=""></a><%=request.getAttribute("ERROR") == null ? "" :
request .getAttribute("ERROR")%></td>
</tr>
<tr>
<td><input type="submit" value="提交" /></td>
</tr>
<div id="wavArea" style="display:none">
</div>
</table>
</form>可以看到,我们采用了不在页面显示的 embed 标签来调用 Servlet 以获取声音流,然后自动播放。在每次页面刷新的时候,还需要将 Session 中的 SoundCaptchaService 的实现对象以及音频流删除,以便页面刷新时,重新生成新的声音验证码。
后台用来验证用户输入的逻辑和图形版的验证方式相似,这里不做介绍,具体参见示例源代码。需要注意的是,由于示例 3 生成的声音验证码中的字符和图形版的字符并不一致,在后台验证的逻辑中,只需要用户输入等于声音验证码或图形验证码的字符,即可认为用户输入正确。下面将介绍如何才能使声音版和图形版中的字符保持一致。
--------------------------------------------------------------------------------
回页首
使声音验证码和图形验证码字符一致
JCaptcha 并没有提供很直观的方式,来保证声音验证码和图形验证码中的字符一致,这就需要我们了解 JCaptcha 的实现,并能够通过继承的方式来扩展定制 JCaptcha,从而实现声音和图形验证码字符的一致。
由于图形验证码会先于声音验证码生成,所以,我们第一步就是需要获取图形验证码所生成的字符串,然后是利用所获取的字符串来生成声音验证码。
获取图形验证码随机数
在以上示例中,虽然我们在实现 ImageCaptchaEngine 和 SoundCaptchaEngine 的时候,可以设置随机数生成类,比如 RandomWordGenerator 等,但是即使声音版和图形版采用同一个随机数生成对象,也不能保证会生成同一个字符,因为它们仅仅是设定一个随机字库,而字符则是每次随机生成。因而,我们并不能通过使用同样的随机数生成对象来生成同样的随机数,只能通过考虑使用图形版生成的字符来生成声音验证码,才能保持两者的一致。
在创建 ImageCaptchaEngine 的实现时,我们需要提供一个 ImageCaptchaFactory,实际上,我们可以通过使用继承实现 JCaptcha 已有 ImageCaptchaFactory 的实现,来获取生成的随机数。清单 9 是示例 4 中继承了 GimpyFactory 的代码片段。
清单 9. SampleGimpyFactory 代码片段
public class SampleGimpyFactory extends GimpyFactory {
……
public ImageCaptcha getImageCaptcha(Locale locale) {
//length
Integer wordLength = getRandomLength();
String word = getWordGenerator().getWord(wordLength, locale);
if (this.wordBridge != null) {
this.wordBridge.setGeneratedWord(word);
}
BufferedImage image = null;
try {
image = getWordToImage().getImage(word);
} catch (Throwable e) {
throw new CaptchaException(e);
}
ImageCaptcha captcha =
new SampleGimpy(CaptchaQuestionHelper.getQuestion(locale, BUNDLE_QUESTION_KEY),
image, word);
return captcha;
}
}可以发现,通过使用上面的 SampleGimpyFactory 即可获取图形验证码的随机数,所以我们可以将清单 6 中的 GimpyFactory 替换为 SampleGimpyFactory。
在获取了生成的随机数后,还需考虑如何将该随机数传递给生成声音验证码的代码。考虑到我们在生成验证码时,都是基于同一个 Session ID 生成,那么我们就可以将生成的随机数放到 map 中,而 key 是 Session ID,那么,在生成验证码字符后,我们就可以用 Session ID 取出该字符串,并放到 Session 中,然后,在生成声音验证码的代码中,就可以从 Session 中获取随机数。但是,并不是所有代码都可以很方便的获取到 ID,所以,我们还需要对示例 2 的代码进行改造,以便能够根据 ID 保存随机数。清单 10 为改造后的清单 5 中的类 SampleImageCaptchaService 的代码片段。
清单 10. SampleImageCaptchaService 代码片段
public class SampleImageCaptchaService extends
AbstractManageableImageCaptchaService implements ImageCaptchaService {
… ..
@Override
public BufferedImage getImageChallengeForID(String ID)
throws CaptchaServiceException {
BufferedImage image= super.getImageChallengeForID(ID);
String generatedWord = ((SampleListImageCaptchaEngine) engine).getWordBridge()
.getGeneratedWord();
WordMap.getWordsMap().put(ID, generatedWord);
return image;
}
}如清单 10 所示,我们将生成的随机数放到了一个 map 中,而 key 则是 ID,这里也就是 SessionID,然后我们就可以在 ImageCaptchaServlet 获取并将该随机数放到 Session 中,如清单 11 所示。接下来便是如何利用该字符生成声音验证码。
清单 11. ImageCaptchaServlet 代码片段
httpServletRequest.getSession().setAttribute(
"generatedWord",
WordMap.getWordsMap().get(httpServletRequest.getSession(true)
.getId()));利用指定字符生成声音验证码
为了能够使用指定的字符串生成验证码,我们需要对示例 3 中的代码做一定的修改。这里,示例 4 通过继承 SpellerSoundFactory 实现了一个扩展的 SoundCaptchaFactory,清单 12 为代码片段。
清单 12. SampleSpellerSoundFactory 代码片段
public class SampleSpellerSoundFactory extends SpellerSoundFactory {
private String word;
……
@Override
public SoundCaptcha getSoundCaptcha() {
return getSoundCaptcha(Locale.getDefault());
}
@Override
public SoundCaptcha getSoundCaptcha(Locale locale) {
String soundWord = "";
if (this.word != null && !this.word.equals("")) {
soundWord = this.word;
} else {
soundWord = this.wordGenerator.getWord(getRandomLength(), locale);
}
AudioInputStream sound = this.word2Sound.getSound(wordDecorator
.decorateWord(soundWord), locale);
SoundCaptcha soundCaptcha = new SpellerSound(getQuestion(locale),
sound, word);
return soundCaptcha;
}
}根据清单 11 中的代码,如果字符串能够正确的传进来,这个 SampleSpellerSoundFactory 将可以根据该字符串生成声音验证码。
相应的,清单 7 中的 SampleListSoundCaptchaEngine 需要做如清单 13 所示的修改。
清单 13. SampleListSoundCaptchaEngine 代码
public class SampleListSoundCaptchaEngine extends ListSoundCaptchaEngine {
private String word;
private static String voiceName = "kevin16";
private static String voicePackage =
"com.sun.speech.freetts.en.us.cmu_us_kal.KevinVoiceDirectory";
protected void buildInitialFactories() {
WordGenerator wordGenerator = new RandomWordGenerator("ABCDEFGHIJKLMNOPQRSTUVWXYZ");
SpellerWordDecorator decorator = new SpellerWordDecorator(", ");
SoundCaptchaFactory soundFactory[] = { new SampleSpellerSoundFactory(
wordGenerator,
new FreeTTSWordToSound(new FreeTTSSoundConfigurator(voiceName,
voicePackage, 1.0f, 100, 100), 4, 10), decorator, word) };
this.setFactories(soundFactory);
}
public SampleListSoundCaptchaEngine(String word) {
this.word = word;
buildInitialFactories();
}
}需要注意的是,我们一定要有如清单 13 所示的带参数的够找函数,否则初始化时,该类会首先调用父类的构造函数,其父类会直接调用 buildInitialFactories 函数,而此时字符串还没有传递给父类。
接下来需要修改 SampleSoundCaptchaService,在该类中使用 SampleListSoundCaptchaEngine 并传入随机数参数。
清单 14. SampleSoundCaptchaService 代码片段
public class SampleSoundCaptchaService extends
AbstractManageableSoundCaptchaService implements SoundCaptchaService {
public SampleSoundCaptchaService(String word) {
super(new FastHashMapCaptchaStore(),
new SampleListSoundCaptchaEngine(word), 180, 100000, 75000);
}
……
}最后,我们只需要修改 SoundCaptchaServlet,先依据 Session ID 获取生成的随机数,然后调用清单 14 的 SampleSoundCaptchaService 生成声音验证码,如清单 15 所示。
清单 15. SoundCaptchaServlet 代码片段
String word = "";
if (httpServletRequest .getSession().getAttribute("generatedWord") != null) {
word = (String)httpServletRequest.getSession().getAttribute("generatedWord");
logger.info("Get generated word from the session, word=" + word);
}
service = new SampleSoundCaptchaService(word);至于后台验证逻辑,可以不做修改,也可以删除原先用于验证声音验证码的代码。至此,声音验证码的字符将与图形验证码的字符保持一致。
--------------------------------------------------------------------------------
回页首
总结
本文先从一个简单的示例入手,介绍了如何采用 JCaptcha 开发一个简单的图形验证码,然后介绍了如何通过扩展定制,实现更为复杂的图形验证码,接下来介绍了如何采用 JCaptcha 开发声音验证码,并在最后,介绍了如何扩展 JCaptcha 来保证图形验证码和声音验证码的内容一致。
发表评论
-
HttpUrlConnection与httpclient的速度
2015-03-10 17:59 892文件越大,可能HttpUrlConnection的速度优势越明 ... -
FastDFS与hadoop的HDFS区别
2015-01-12 16:12 4219主要是定位和应用场合不一样。 hadoop的文件系统HDFS主 ... -
RequestDispatcher实现文件下载
2015-01-04 14:55 764本来我使用的是文件流下载的方式,在Tomcat下可行,但是在W ... -
javax.mail.MessagingException: 501 5.0.0 HELO requires domain address
2014-12-22 17:32 7http://zouhuajian01.blog.163.co ... -
javax.mail.MessagingException: 501 5.0.0 HELO requires domain address
2014-12-22 17:32 1078http://zouhuajian01.blog.163.co ... -
https协议网页能够被搜索引擎收录吗?
2014-11-12 17:07 570百度现在只能收录少部分的https,大部分的https网页无法 ... -
aes加解密
2014-10-29 13:18 742import java.io.File; import ja ... -
udp测试
2014-10-22 15:39 511udp,常用于聊天室,直接向服务发送信息,不进行3次握手。 服 ... -
aio测试
2014-10-22 14:22 705由操作系统来做异步 服务端: package aio; ... -
fastdfs使用实战(Java实例篇)
2014-09-29 18:11 22546一、创建一个maven的webproject,叫file-ma ... -
谷歌(Chrome)安装Advanced REST Client插件
2014-09-29 10:44 2730以前用过jmeter测试各种url连接,soapui测试web ... -
sftp工具类
2014-09-28 13:29 965import java.io.File; import ja ... -
quartz配置
2014-09-22 10:35 386以前做过好几个quartz的应用项目,但都没有记录,当再次用到 ... -
ftp工具类
2014-09-19 18:08 749每回用到总去网上找一通,还是自已总结下比较好 package ... -
Joda-Time 简介
2014-08-18 10:01 527iteye转的文章与自已的文章,不能放到一起。真麻烦。 转一个 ... -
log.isDebugEnabled()
2014-08-06 11:55 764在使用log4j,common-log这样的log框架时,发现 ... -
zookeeper与activemq最新存储replicatedLevelDB整合
2014-08-01 19:57 7032测试环境:三台VM虚拟机centos6.4 64位 mini版 ... -
一致性哈希算法原理 .
2014-08-01 19:53 563http://baike.baidu.com/view/158 ... -
map,xml互转
2014-06-24 11:46 95981.这个转出来会有很多空格package cn.paypalm ... -
json,xml互转
2014-06-20 19:00 612XMLSerializer xmlSerializer = n ...
相关推荐
在Java开发中,JCaptcha是一个广泛使用的开源库,它提供了一种简单且灵活的方式来生成和验证这种人机验证图像。 1. **什么是JCaptcha?** JCaptcha,全称为Java CAPTCHA,是由Greg Wilkins创建的一个强大的验证码...
jCaptcha 是一个基于 Java 的开源验证码库,它提供了丰富的图形验证码解决方案。这个库的特点在于可以创建多彩且复杂的图像,这使得机器难以识别,但对人类来说相对容易辨认。 jCaptcha 的主要优点包括: 1. **可...
在这个"基于SSM实现的注册登录系统"项目中,验证码使用了jcaptcha库,邮件发送则借助了JEmail,这些技术的应用使得系统的安全性和用户体验得到了提升。 **1. SSM框架** - **Spring**:作为基础容器,管理着应用的...
Jcaptcha的核心功能包括生成随机且难以自动识别的图形验证码,以及验证用户输入的验证码是否与生成的原始验证码匹配。以下是对Jcaptcha使用过程中涉及的关键知识点的详细说明: 1. **安装和引入Jcaptcha库**: - ...
Java验证码生成库JCaptcha是一个强大的工具,用于在Web应用程序中创建和验证安全的图形验证码。验证码的主要目的是防止自动机器人和恶意脚本进行未经授权的操作,如批量注册、恶意登录尝试等。下面将详细介绍...
6. **安全性**:Jcaptcha采用安全的哈希算法来存储和验证验证码,确保每次请求的验证码都是唯一的,并且在使用后立即失效。 ### 使用Jcaptcha的步骤 1. **添加依赖**:首先,你需要将Jcaptcha的库文件添加到你的...
最常见的可能就是图形验证码,它通常由一串随机字符组成,显示在一个扭曲或带有干扰线的图片上,用户需要正确输入图片上的字符才能通过验证。 实现图形验证码的关键步骤包括: 1. **生成随机验证码**:可以使用...
它提供了一种简单的方式来实现图形验证码的功能,增强了系统的安全性。 - **安装与配置**: - 添加Jcaptcha依赖到`pom.xml`文件中: ```xml <groupId>com.octo.captcha</groupId> <artifactId>jcaptcha ...
JCaptcha是一个开源的验证码库,它提供了更丰富的图形验证码生成选项,如扭曲、噪声、背景等,增加了破解的难度。使用JCaptcha的步骤如下: 1. 添加JCaptcha的依赖到项目中。 2. 创建一个JCaptcha配置类,设置验证码...
Java Web 开发中的图形验证码实现涉及多个关键步骤和技术: 1. **生成随机字符串**:首先,我们需要生成一个随机的字符串作为验证码内容。这个字符串通常由字母、数字或两者混合组成,长度在4到6个字符之间,以确保...
- 如何生成复杂的图像:Jcaptcha如何使用数学算法和图形库创建难以自动识别的图像。 - 验证码的存储与检索:Jcaptcha如何与session或cookie交互,以保存和验证验证码。 - 性能优化:Jcaptcha在处理大量请求时如何...
在Eclipse中,开发者可以利用Java的相关库,如Apache Commons Codec库来生成随机字符串,或者使用Java的图形库来绘制图像验证码。 在Java开发中,创建验证码通常包括以下步骤: 1. 生成随机字符串:这可以通过Java...
在Java中,我们可以使用多种库来生成验证码,例如 JCaptcha 和 EasyCaptcha。这些库提供了丰富的功能,包括设置验证码的长度、字符集、扭曲程度、噪点等。本压缩包中的示例可能是基于这些库或者自定义实现的。 在...
这通常涉及到使用Java的图形库(如Java AWT或JavaFX)来绘制随机文本和图形。你需要确保生成的验证码字符串安全地存储在服务器端,并且在用户提交表单时进行验证。 3. **CSS和JS**:在验证码的实现中,CSS用于美化...
开发人员可能会使用`java.awt`和`java.awt.image`包来创建图像,使用`java.util.Random`来生成随机字符。 4. **验证码源码分析**: 源码通常会包含以下几个关键部分: - 字符生成:随机选择字母、数字或其他字符...
3. **图形验证码**:更复杂的验证码形式是图形验证码,它涉及到图像处理。Java的`java.awt`和`javax.imageio`包提供了图像创建、绘制和保存的功能。你可以创建一个空白的图像,然后在上面绘制随机的颜色、线条、噪声...
- **绘制验证码**:可以使用Java的Graphics类或者第三方库(如JCaptcha)来绘制验证码图像,包括字符、背景、干扰线等元素。 - **存储与验证**:生成的验证码字符串要存储在服务器端,当用户输入后,服务器会比对...
在网页开发中,验证码是一种广泛使用的安全机制,用于防止自动化的机器人或恶意脚本进行非法操作,如批量注册、垃圾信息提交等。"jsp彩色文字验证码"是基于Java Server Pages (JSP)技术实现的一种验证码生成方法,它...
图文验证码通常包含随机生成的图像和对应的字符,图像中的字符可能是扭曲、旋转、模糊或叠加在其他图形之上,以增加机器识别的难度。 要创建一个Java实现的图文验证码,你需要以下几个步骤: 1. **生成随机字符**...
- 在Java中,可以使用`java.awt`和`javax.imageio`库来生成图形验证码。首先创建一个BufferedImage对象,然后使用Graphics类绘制随机颜色的背景、随机形状和噪声,最后将生成的字符串写入图像。 3. **存储验证码**...