jcaptcha是一款很好的java實現的驗證碼組件,官方地址:http://jcaptcha.sourceforge.net/
在使用jcaptcha過程當中,我相信不少人在用ajax校驗驗證碼的時候都遇到過下面這樣的異常:
java
Invalid ID, could not validate unexisting or already validated captchaajax
其實分析jcaptcha源碼不可貴知,這個異常是由於AbstractCaptchaService中的validateResponseForID方法邏輯在搗亂,看看源碼:session
public Boolean validateResponseForID(String ID, Object response) throws CaptchaServiceException { if (!this.store.hasCaptcha(ID)) { throw new CaptchaServiceException("Invalid ID, could not validate unexisting or already validated captcha"); } Boolean valid = this.store.getCaptcha(ID).validateResponse(response); this.store.removeCaptcha(ID); return valid; }
其中有句this.store.removeCaptcha(ID);,就是這句代碼在搗亂。這句代碼的意思就是說,無論何時,一檔執行了validateResponseForID方法,就會把原來的那個session從store當中移除,那麼這樣,若是你採用的是ajax提交校驗的話,驗證碼生成邏輯沒有從新執行,就會報上面的Invalid ID異常,若是是校驗失敗就從新刷新頁面從新生成應該就問題,沒有驗證是否是這樣,道理就是這麼個道理了……
那麼爲了知足ajax校驗,咱們作點稍微的改造:
首先咱們若是採用的GenericManageableCaptchaService做爲CaptchaService實現類,我就自定義一個Service來繼承GenericManageableCaptchaService,代碼以下:ide
public class CustomGenericManageableCaptchaService extends GenericManageableCaptchaService { /** * The constructor method of class CustomManageableCaptchaService.java . * * @param captchaEngine * @param minGuarantedStorageDelayInSeconds * @param maxCaptchaStoreSize * @param captchaStoreLoadBeforeGarbageCollection */ public CustomGenericManageableCaptchaService(CaptchaEngine captchaEngine, int minGuarantedStorageDelayInSeconds, int maxCaptchaStoreSize, int captchaStoreLoadBeforeGarbageCollection) { super(captchaEngine, minGuarantedStorageDelayInSeconds, maxCaptchaStoreSize, captchaStoreLoadBeforeGarbageCollection); } /** * The constructor method of class CustomManageableCaptchaService.java . * * @param captchaStore * @param captchaEngine * @param minGuarantedStorageDelayInSeconds * @param maxCaptchaStoreSize * @param captchaStoreLoadBeforeGarbageCollection */ public CustomGenericManageableCaptchaService(CaptchaStore captchaStore, CaptchaEngine captchaEngine, int minGuarantedStorageDelayInSeconds, int maxCaptchaStoreSize, int captchaStoreLoadBeforeGarbageCollection) { super(captchaStore, captchaEngine, minGuarantedStorageDelayInSeconds, maxCaptchaStoreSize, captchaStoreLoadBeforeGarbageCollection); } /** * 修改驗證碼校驗邏輯,默認的是執行了該方法後,就把sessionid從store當中移除<br/> * 然而在ajax校驗的時候,若是第一次驗證失敗,第二次還得從新刷新驗證碼,這種邏輯不合理<br/> * 如今修改邏輯,只有校驗經過之後,才移除sessionid。 Method Name:validateResponseForID . * * @param ID * @param response * @return * @throws CaptchaServiceException * the return type:Boolean */ @Override public Boolean validateResponseForID(String ID, Object response) throws CaptchaServiceException { if (!this.store.hasCaptcha(ID)) { throw new CaptchaServiceException( "Invalid ID, could not validate unexisting or already validated captcha"); } Boolean valid = this.store.getCaptcha(ID).validateResponse(response); //源碼的這一句是沒被註釋的,這裏咱們註釋掉,在下面暴露一個方法給咱們本身來移除sessionId //this.store.removeCaptcha(ID); return valid; } /** * 移除session綁定的驗證碼信息. * Method Name:removeCaptcha . * @param sessionId * the return type:void */ public void removeCaptcha(String sessionId){ if(sessionId!=null && this.store.hasCaptcha(sessionId)){ this.store.removeCaptcha(sessionId); } } }
從代碼中咱們能夠看到兩個改造:
一、重寫了validateResponseForID方法,只是註釋了一行代碼;
二、增長了一個方法removeCaptcha,用來讓咱們手工移除session對應的驗證碼信息this
而後就把bean對應的實現類指定到咱們的自定義實現上,以下:spa
<bean id="captchaService" class="com.cmcc.ict.iplanning.captcha.CustomGenericManageableCaptchaService"> <constructor-arg index="0"><ref bean="imageEngine"/></constructor-arg> <constructor-arg index="1"><value>180</value></constructor-arg> <constructor-arg index="2"><value>180000</value></constructor-arg> <constructor-arg index="3"><value>75000</value></constructor-arg> </bean>
最後就是在你的業務邏輯裏面手動調用removeCapthca()方法了,例如:.net
try {code
currentUser.login(token);繼承
....token
//成功之後移除驗證碼信息
captchaService.removeCaptcha(sessionId);
} catch (UnknownAccountException uae) {
....
} catch (IncorrectCredentialsException ice) {
....
} catch (LockedAccountException lae) {
....
} catch (ExcessiveAttemptsException eae) {
....
} catch (AuthenticationException ae) {
....
}