JCaptcha 簡介
CAPTCHA 全稱 Completely Automated Public Turing Test to Tell Computers and Humans Apart哈打,最早作為卡內基梅隆大學的一個科研項目,用于生成一個人類容易通過而計算機難以通過的測試僵芹,目前廣泛應用于網(wǎng)絡應用档押,用于阻止機器人發(fā)布垃圾信息淫痰。JCaptcha 即為 Java 版本的 CAPTCHA 項目东揣,其是一個開源項目揽咕,支持生成圖形和聲音版的驗證碼,在生成聲音版的驗證碼時粟关,需要使用到 FreeTTS疮胖。目前,JCaptcha 官方網(wǎng)站顯示有 2.0 版本闷板,但二進制版只有 1.0 版可供下載澎灸,本文亦是基于 1.0 版本展開
一個簡單的圖形驗證碼
JCaptcha 提供了一定的可擴展能力,用于開發(fā)人員創(chuàng)建出復雜的圖形驗證碼遮晚。下面性昭,首先利用 JCaptcha 提供的 API 來快速開發(fā)一個簡單示例。本文的示例為 Web 應用县遣,可以運行在 Tomcat 和 WebSphere 上巩梢,除了準備 Web 服務器外,我們還需要準備好 JCaptcha 運行時所必須的 Jar 包艺玲。
準備所需的 Jar 包
JCaptcha 項目在實現(xiàn)中括蝠,還引用了 commons-collections 和 commons-logging 兩個開源項目,再加上 JCaptcha 本身的實現(xiàn)饭聚,我們共需要三個包忌警,具體信息如下:
jcaptcha-1.0-all.jar
commons-logging-1.1.1.jar
commons-collections-3.2.jar
創(chuàng)建生成圖形驗證碼的 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 的默認實現(xiàn)類 DefaultManageableImageCaptchaService探越,在代碼中狡赐,我們需要通過獲取 service 對象,根據(jù) Session ID 來生成驗證碼圖片钦幔。除了使用該默認實現(xiàn)類外枕屉,我們也可以通過實現(xiàn) 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 為用于顯示圖形驗證碼的頁面代碼片段喷市。
<form action = "CaptchaValidationServlet" method="post">
<table>
? <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>
除了實現(xiàn)圖形驗證碼的生成和展示,我們還需要一個 Servlet 來驗證用戶的輸入咆蒿。用戶輸入驗證的代碼的核心东抹,是使用在生成圖片時所使用的 service 對象蚂子,調用 JCaptcha 的輸入驗證方法(本示例為 validateResponseForID 方法)沃测,來判斷用戶輸入是否正確,清單 4 為輸入驗證代碼片段食茎。
清單 4. 驗證用戶輸入
?validated = service.validateResponseForID(
request.getSession(true).getId(), userCaptchaResponse).booleanValue();
完成上述代碼工作后蒂破,我們還需要在 Web.xml 中設置好上面創(chuàng)建的兩個 Servlet,然后再部署到 Web 服務器上别渔,圖 1 是部署成功后的示例 1 首頁附迷。
至此,我們完成了第一個示例哎媚,該示例采用了 JCaptcha 提供的默認實現(xiàn)喇伯,下面我們將通過擴展使用 JCaptcha,來生成一個更為復雜的圖形驗證碼拨与。
使用 JCaptcha 生成復雜圖形驗證碼
在實際項目中稻据,我們可能需要使用更為復雜的圖形驗證碼,比如买喧,加入帶干擾的圖形背景捻悯、更多的干擾點、自定義隨機字庫及圖形字體等等淤毛,下面將基于示例 2 介紹如何使用 JCaptcha 生成更為復雜的圖形驗證碼今缚。
自定義 ImageCaptchaService 的實現(xiàn)
正如介紹示例 1 時所提到的,我們可以通過實現(xiàn) ImageCaptchaService 接口來創(chuàng)建我們自己所需要的 service 對象低淡,清單 5 為示例 2 對接口 ImageCaptchaService 的實現(xiàn)姓言,我們可以在該實現(xiàn)中,使用自己開發(fā)的圖形驗證碼生成引擎(SampleListImageCaptchaEngine)來生成更為復雜的圖形驗證碼蔗蹋。
清單 5. 示例 2 中 ImageCaptchaService 的實現(xiàn)
?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 用于存放生成的驗證碼字符事期,F(xiàn)astHashMapCaptchaStore 基本可以滿足絕大多數(shù)項目需求,我們通常不需要創(chuàng)建自己的類來實現(xiàn)它的功能纸颜;SampleListImageCaptchaEngine 則是擴展的核心兽泣,該類是 ImageCaptchaEngine 的一個實現(xiàn),在該類中胁孙,我們可以設置隨機字庫唠倦、生成的字體和大小称鳞、字符數(shù)、背景圖片以及干擾點等等稠鼻。清單 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 的第一個參數(shù)用于設置驗證碼最少字符數(shù)候齿,第二個參數(shù)為最多的字符數(shù)熙暴,第三個參數(shù) SingleColorGenerator 為字體顏色,這里為黑色慌盯,TextDecorator 為干擾設置周霉,這里是一個字符一個干擾點,并且干擾點為白色亚皂。
在 ImageCaptchaFactory 中俱箱,第一個參數(shù)設置了隨機字庫,這里為英文字母灭必,在第二個參數(shù)中狞谱,TwistedRandomFontGenerator 設置了生成的字符字體,最小 34禁漓,最大為 40跟衅,F(xiàn)unkyBackgroundGenerator 則用于生成干擾背景,除了設置字體大小外播歼,還需要設置生成的圖片大小伶跷,示例 2 為 260*70 像素。圖 2 為示例 2 的首頁截圖荚恶。
圖 2. 示例 2 的首頁
可以說 ImageCaptchaEngine 的不同實現(xiàn)撩穿,決定了圖形驗證碼的不同樣式,JCaptcha 目前提供了多種 ImageCaptchaEngine 的實現(xiàn)谒撼,這些實現(xiàn)提供了多種不同的圖形驗證碼樣式食寡,當然,我們也可以自己實現(xiàn) TextPaster 及 TextDecorator 等廓潜,進而再繼承實現(xiàn) ImageCaptchaEngine抵皱,從而實現(xiàn)自己所期望的效果。圖 3 為 JCaptcha 官方網(wǎng)站提供的部分 ImageCaptchaEngine 的實現(xiàn)截圖辩蛋,更多樣式呻畸,請參見官方網(wǎng)站。
圖 3. JCaptcha 官方圖形驗證碼示例
開發(fā)聲音驗證碼
由于某些項目需要支持盲人使用悼院,而盲人無法看到圖形驗證碼伤为,這時,我們就需要同時提供聲音版的驗證碼。JCaptcha 同時支持聲音版驗證碼绞愚,但默認實現(xiàn)并不支持生成的聲音和圖形驗證碼中的字符一致叙甸,這里就需要通過一定的擴展定制,才能保證聲音和圖形驗證碼的內容一致位衩,下面首先介紹如何生成聲音驗證碼裆蒸,然后介紹如何擴展定制,才能保證聲音和圖形驗證碼的內容的一致糖驴。
FreeTTS
JCaptcha 使用了 FreeTTS 來開發(fā)聲音驗證碼僚祷,F(xiàn)reeTTS 是一個采用 Java 編寫的語音合成項目,它基于 Flite 項目編寫贮缕,F(xiàn)lite 項目是一個由卡內基梅隆大學開發(fā)的小型語音合成引擎辙谜,其又源于愛丁堡大學的 Festival 語音合成系統(tǒng)和卡內基梅隆大學的 FestVox 項目。本文的示例使用了 FreeTTS 1.2.2跷睦,該版本的有如下幾個方面的功能:
一個語音合成引擎
支持多種聲音
1 個 8khz筷弦,多音位肋演,男性美國英語發(fā)音
1 個 16khz抑诸,多音位,男性美國英語發(fā)音
1 個 16khz 有限聲音域的男性美國英語發(fā)音
支持從 FestVox 導入聲音(僅支持美國英語)
一定程度上支持從 FestVox 中導入 CMU ARCTIC 聲音
支持 MBROLA 聲音(需另外下載該功能包)
1 個 16khz 女性美國英語發(fā)音
2 個 16khz 男性美國英語發(fā)音
部分支持 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:用于 FreeTTS
commons-logging-1.1.1.jar:用于 JCaptcha
commons-collections-3.2.jar:用于 JCaptcha
cmutimelex.jar:用于 FreeTTS
cmulex.jar:用于 FreeTTS
cmudict04.jar:用于 FreeTTS
cmu_us_kal.jar:用于 FreeTTS
cmu_time_awb.jar:用于 FreeTTS
構建生成聲音的 SoundCaptchaEngine
同 ImageCaptchaEngine 一樣反症,JCaptcha 同時提供了 SoundCaptchaEngine辛块,所以整個聲音驗證碼的核心是創(chuàng)建 SoundCaptchaEngine 的實現(xiàn),當然 JCaptcha 也提供了一些默認的實現(xiàn)铅碍,比如 DefaultSoundCaptchaEngine 以及 SpellerSoundCaptchaEngine 等等润绵,為了考慮能夠支持生成和圖形驗證碼相同的字符,示例 3 采用繼承抽象類 ListSoundCaptchaEngine 的方式胞谈,來創(chuàng)建自己的實現(xiàn) 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);
?}
}
創(chuàng)建生成聲音驗證碼的 Servlet
同開發(fā)圖形驗證碼一樣烦绳,這里也需要創(chuàng)建 SoundCaptchaService 的實現(xiàn)卿捎,實現(xiàn)方式與 ImageCaptchaService 的實現(xiàn)類似,示例 3 的實現(xiàn)類為 SampleSoundCaptchaService径密,這里不做敘述午阵,讀者可以參見附帶的示例源代碼。
和 ImageCaptchaService 的實現(xiàn)不同的是享扔,這里不能采用單例的方式來獲取 SoundCaptchaService 的實現(xiàn)底桂,否則不能多次為同一 Session ID 生成多個聲音驗證碼文件括细。另外,在用戶不刷新頁面戚啥,而重復點擊聲音驗證碼圖標時奋单,我們需要提供同一段聲音,因此猫十,我們可以將 SoundCaptchaService 的實現(xiàn)對象以及所生成的字節(jié)碼流放入到 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();
? }
創(chuàng)建 Web 頁面和驗證代碼
為了方便用戶使用拖云,我們需要在頁面上放置一個圖標贷笛,當焦點落在圖標上(考慮到盲人可能使用鍵盤 Tab 鍵來切換焦點),觸發(fā) onFocus 事件來調用 JavaScript 方法顯示 embed 標簽宙项,從而調用上面生成聲音驗證碼的 Servlet乏苦。圖 4 為示例 3 首頁截圖。
圖 4. 示例 3 運行頁面截圖
當聲音驗證碼圖標在已經獲取焦點尤筐,并朗讀后汇荐,用戶可能多次重復讓該圖標獲取焦點以便多次收聽,這時盆繁,JavaScript 方法會被多次觸發(fā)掀淘,如果不做處理,其將多次請求 Servlet油昂,以致生成不同的聲音文件革娄,本示例采用了前一個小節(jié)所介紹的緩存 SoundCaptchaService 對象和字節(jié)碼流的方法來解決這個問題,以保證多次能收聽到同一段聲音冕碟。當然拦惋,也可以采用在 URL 后面加一個隨機 token 的方式,來判斷是否重復提交安寺。清單 8 為示例 3 首頁代碼片段厕妖。
清單 9. 示例 3 index.jsp 代碼片段
<%request.getSession().removeAttribute("soundService");
request.getSession().removeAttribute("stream");
%>
<html>
<head>
<script type = "text/javascript">
functionplaySoundCaptcha() {
varwavURL ='<%= request.getContextPath() %>'+'/SoundCaptchaServlet';
varembedAudioPlayer ="";
varwavArea = document.getElementById("wavArea");
wavArea.innerHTML = embedAudioPlayer;
}
</script>
</head>
<form action="CaptchaValidationServlet" method="post">
<table>
<tr>
? ? <td colspan="2"><iimgsrc="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" ></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 的實現(xiàn)對象以及音頻流刪除挠羔,以便頁面刷新時井仰,重新生成新的聲音驗證碼。
后臺用來驗證用戶輸入的邏輯和圖形版的驗證方式相似破加,這里不做介紹俱恶,具體參見示例源代碼。需要注意的是,由于示例 3 生成的聲音驗證碼中的字符和圖形版的字符并不一致合是,在后臺驗證的邏輯中了罪,只需要用戶輸入等于聲音驗證碼或圖形驗證碼的字符,即可認為用戶輸入正確聪全。下面將介紹如何才能使聲音版和圖形版中的字符保持一致泊藕。
使聲音驗證碼和圖形驗證碼字符一致
JCaptcha 并沒有提供很直觀的方式,來保證聲音驗證碼和圖形驗證碼中的字符一致难礼,這就需要我們了解 JCaptcha 的實現(xiàn)娃圆,并能夠通過繼承的方式來擴展定制 JCaptcha,從而實現(xiàn)聲音和圖形驗證碼字符的一致蛾茉。
由于圖形驗證碼會先于聲音驗證碼生成讼呢,所以,我們第一步就是需要獲取圖形驗證碼所生成的字符串谦炬,然后是利用所獲取的字符串來生成聲音驗證碼悦屏。
獲取圖形驗證碼隨機數(shù)
在以上示例中,雖然我們在實現(xiàn) ImageCaptchaEngine 和 SoundCaptchaEngine 的時候键思,可以設置隨機數(shù)生成類础爬,比如 RandomWordGenerator 等,但是即使聲音版和圖形版采用同一個隨機數(shù)生成對象稚机,也不能保證會生成同一個字符幕帆,因為它們僅僅是設定一個隨機字庫获搏,而字符則是每次隨機生成赖条。因而,我們并不能通過使用同樣的隨機數(shù)生成對象來生成同樣的隨機數(shù)常熙,只能通過考慮使用圖形版生成的字符來生成聲音驗證碼纬乍,才能保持兩者的一致。
在創(chuàng)建 ImageCaptchaEngine 的實現(xiàn)時裸卫,我們需要提供一個 ImageCaptchaFactory仿贬,實際上,我們可以通過使用繼承實現(xiàn) JCaptcha 已有 ImageCaptchaFactory 的實現(xiàn)墓贿,來獲取生成的隨機數(shù)茧泪。清單 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;
?? }
}
可以發(fā)現(xiàn)聋袋,通過使用上面的 SampleGimpyFactory 即可獲取圖形驗證碼的隨機數(shù)队伟,所以我們可以將清單 6 中的 GimpyFactory 替換為 SampleGimpyFactory。
在獲取了生成的隨機數(shù)后幽勒,還需考慮如何將該隨機數(shù)傳遞給生成聲音驗證碼的代碼嗜侮。考慮到我們在生成驗證碼時,都是基于同一個 Session ID 生成锈颗,那么我們就可以將生成的隨機數(shù)放到 map 中,而 key 是 Session ID,那么承二,在生成驗證碼字符后瘟芝,我們就可以用 Session ID 取出該字符串,并放到 Session 中覆醇,然后绅喉,在生成聲音驗證碼的代碼中,就可以從 Session 中獲取隨機數(shù)叫乌。但是柴罐,并不是所有代碼都可以很方便的獲取到 ID,所以憨奸,我們還需要對示例 2 的代碼進行改造革屠,以便能夠根據(jù) ID 保存隨機數(shù)。清單 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 所示似芝,我們將生成的隨機數(shù)放到了一個 map 中,而 key 則是 ID板甘,這里也就是 SessionID党瓮,然后我們就可以在 ImageCaptchaServlet 獲取并將該隨機數(shù)放到 Session 中,如清單 11 所示盐类。接下來便是如何利用該字符生成聲音驗證碼寞奸。
清單 11. ImageCaptchaServlet 代碼片段
httpServletRequest.getSession().setAttribute(
"generatedWord",
WordMap.getWordsMap().get(httpServletRequest.getSession(true)
.getId()));
利用指定字符生成聲音驗證碼
為了能夠使用指定的字符串生成驗證碼,我們需要對示例 3 中的代碼做一定的修改在跳。這里枪萄,示例 4 通過繼承 SpellerSoundFactory 實現(xiàn)了一個擴展的 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;
? }
}
根據(jù)清單 11 中的代碼瓷翻,如果字符串能夠正確的傳進來,這個 SampleSpellerSoundFactory 將可以根據(jù)該字符串生成聲音驗證碼割坠。
相應的齐帚,清單 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 所示的帶參數(shù)的夠找函數(shù)对妄,否則初始化時,該類會首先調用父類的構造函數(shù)沪羔,其父類會直接調用 buildInitialFactories 函數(shù)饥伊,而此時字符串還沒有傳遞給父類象浑。
接下來需要修改 SampleSoundCaptchaService,在該類中使用 SampleListSoundCaptchaEngine 并傳入隨機數(shù)參數(shù)琅豆。
清單 14. SampleSoundCaptchaService 代碼片段
?public class SampleSoundCaptchaService extends
AbstractManageableSoundCaptchaService implements SoundCaptchaService {
public SampleSoundCaptchaService(String word) {
super(new FastHashMapCaptchaStore(),
new SampleListSoundCaptchaEngine(word), 180, 100000, 75000);
? ? }
……
? }
最后愉豺,我們只需要修改 SoundCaptchaServlet,先依據(jù) Session ID 獲取生成的隨機數(shù)茫因,然后調用清單 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);
至于后臺驗證邏輯冻押,可以不做修改驰贷,也可以刪除原先用于驗證聲音驗證碼的代碼。至此洛巢,聲音驗證碼的字符將與圖形驗證碼的字符保持一致括袒。
總結
Jpatchca生成驗證碼感覺不好使,對圖的參數(shù)控制不好稿茉,可能導致圖很高锹锰,但是文字卻根本沒占滿,如果文字設置大了漓库,會拋出異常恃慧,說文字太高了。
其二渺蒿,Jpatchca不支持集群環(huán)境痢士,默認的驗證碼不是保存在session中,如果想做個性化的處理很麻煩茂装。其實我想要的就是一個聲稱圖片的流就ok了怠蹂,剩下的事情就交給程序員自己實現(xiàn)吧。