前些天發(fā)現(xiàn)了一個巨牛的人工智能學(xué)習(xí)網(wǎng)站,通俗易懂,風(fēng)趣幽默,忍不住分享一下給大家。點擊跳轉(zhuǎn)到教程。
在做用戶登錄功能時,很多時候都需要驗證碼支持,驗證碼的目的是為了防止機(jī)器人模擬真實用戶登錄而惡意訪問,如暴力破解用戶密碼/惡意評論等。目前也有一些驗證碼比較簡單,通過一些OCR工具就可以解析出來;另外還有一些驗證碼比較復(fù)雜(一般通過如扭曲、加線條/噪點等干擾)防止OCR工具識別;但是在中國就是人多,機(jī)器干不了的可以交給人來完成,所以在中國就有很多打碼平臺,人工識別驗證碼;因此即使比較復(fù)雜的如填字、算數(shù)等類型的驗證碼還是能識別的。所以驗證碼也不是絕對可靠的,目前比較可靠還是手機(jī)驗證碼,但是對于用戶來說相對于驗證碼還是比較麻煩的。
?
對于驗證碼圖片的生成,可以自己通過如Java提供的圖像API自己去生成,也可以借助如JCaptcha這種開源Java類庫生成驗證碼圖片;JCaptcha提供了常見的如扭曲、加噪點等干擾支持。
?
一、添加JCaptcha依賴?
Java代碼??
<dependency>??????<groupId>com.octo.captcha</groupId>??????<artifactId>jcaptcha</artifactId>??????<version>2.0-alpha-1</version>??</dependency>??<dependency>??????<groupId>com.octo.captcha</groupId>??????<artifactId>jcaptcha-integration-simple-servlet</artifactId>??????<version>2.0-alpha-1</version>??????<exclusions>??????????<exclusion>??????????????<artifactId>servlet-api</artifactId>??????????????<groupId>javax.servlet</groupId>??????????</exclusion>??????</exclusions>??</dependency>???
com.octo.captcha . jcaptcha?提供了jcaptcha?核心;而jcaptcha-integration-simple-servlet提供了與Servlet集成。
?
二、GMailEngine
來自https://code.google.com/p/musicvalley/source/browse/trunk/musicvalley/doc/springSecurity/springSecurityIII/src/main/java/com/spring/security/jcaptcha/GMailEngine.java?spec=svn447&r=447(目前無法訪問了),仿照J(rèn)Captcha2.0編寫類似GMail驗證碼的樣式;具體請參考com.github.zhangkaitao.shiro.chapter22.jcaptcha.GMailEngine。
?
三、MyManageableImageCaptchaService
提供了判斷倉庫中是否有相應(yīng)的驗證碼存在。?
Java代碼??
public?class?MyManageableImageCaptchaService?extends?????DefaultManageableImageCaptchaService?{???????public?MyManageableImageCaptchaService(????????com.octo.captcha.service.captchastore.CaptchaStore?captchaStore,??????????????com.octo.captcha.engine.CaptchaEngine?captchaEngine,????????int?minGuarantedStorageDelayInSeconds,?????????int?maxCaptchaStoreSize,?????????int?captchaStoreLoadBeforeGarbageCollection)?{??????????super(captchaStore,?captchaEngine,?minGuarantedStorageDelayInSeconds,???????????????maxCaptchaStoreSize,?captchaStoreLoadBeforeGarbageCollection);??????}??????public?boolean?hasCapcha(String?id,?String?userCaptchaResponse)?{??????????return?store.getCaptcha(id).validateResponse(userCaptchaResponse);??????}??}??
??
?
四、JCaptcha工具類
提供相應(yīng)的API來驗證當(dāng)前請求輸入的驗證碼是否正確。??
Java代碼??
public?class?JCaptcha?{??????public?static?final?MyManageableImageCaptchaService?captchaService??????????????=?new?MyManageableImageCaptchaService(new?FastHashMapCaptchaStore(),???????????????????????????????new?GMailEngine(),?180,?100000,?75000);??????public?static?boolean?validateResponse(??????????HttpServletRequest?request,?String?userCaptchaResponse)?{??????????if?(request.getSession(false)?==?null)?return?false;??????????boolean?validated?=?false;??????????try?{??????????????String?id?=?request.getSession().getId();??????????????validated?=???????????????????captchaService.validateResponseForID(id,?userCaptchaResponse)??????????????????????????????.booleanValue();??????????}?catch?(CaptchaServiceException?e)?{??????????????e.printStackTrace();??????????}??????????return?validated;??????}???????public?static?boolean?hasCaptcha(??????????HttpServletRequest?request,?String?userCaptchaResponse)?{??????????if?(request.getSession(false)?==?null)?return?false;??????????boolean?validated?=?false;??????????try?{??????????????String?id?=?request.getSession().getId();??????????????validated?=?captchaService.hasCapcha(id,?userCaptchaResponse);??????????}?catch?(CaptchaServiceException?e)?{??????????????e.printStackTrace();??????????}??????????return?validated;??????}??}???
validateResponse():驗證當(dāng)前請求輸入的驗證碼否正確;并從CaptchaService中刪除已經(jīng)生成的驗證碼;
hasCaptcha():驗證當(dāng)前請求輸入的驗證碼是否正確;但不從CaptchaService中刪除已經(jīng)生成的驗證碼(比如Ajax驗證時可以使用,防止多次生成驗證碼);
?
五、JCaptchaFilter
用于生成驗證碼圖片的過濾器。??
Java代碼??
public?class?JCaptchaFilter?extends?OncePerRequestFilter?{??????protected?void?doFilterInternal(HttpServletRequest?request,?HttpServletResponse?response,?FilterChain?filterChain)?throws?ServletException,?IOException?{????????????response.setDateHeader("Expires",?0L);??????????response.setHeader("Cache-Control",?"no-store,?no-cache,?must-revalidate");??????????response.addHeader("Cache-Control",?"post-check=0,?pre-check=0");??????????response.setHeader("Pragma",?"no-cache");??????????response.setContentType("image/jpeg");??????????String?id?=?request.getRequestedSessionId();??????????BufferedImage?bi?=?JCaptcha.captchaService.getImageChallengeForID(id);??????????ServletOutputStream?out?=?response.getOutputStream();??????????ImageIO.write(bi,?"jpg",?out);??????????try?{??????????????out.flush();??????????}?finally?{??????????????out.close();??????????}??????}??}???
CaptchaService使用當(dāng)前會話ID當(dāng)作key獲取相應(yīng)的驗證碼圖片;另外需要設(shè)置響應(yīng)內(nèi)容不進(jìn)行瀏覽器端緩存。?
?
Java代碼??
<!--?驗證碼過濾器需要放到Shiro之后?因為Shiro將包裝HttpSession?如果不,可能造成兩次的sesison?id?不一樣?-->??<filter>????<filter-name>JCaptchaFilter</filter-name>????<filter-class>???????com.github.zhangkaitao.shiro.chapter22.jcaptcha.JCaptchaFilter????</filter-class>????</filter>????<filter-mapping>??????<filter-name>JCaptchaFilter</filter-name>??????<url-pattern>/jcaptcha.jpg</url-pattern>??</filter-mapping>???
這樣就可以在頁面使用/jcaptcha.jpg地址顯示驗證碼圖片。
?
六、JCaptchaValidateFilter
用于驗證碼驗證的Shiro過濾器。??
Java代碼??
public?class?JCaptchaValidateFilter?extends?AccessControlFilter?{??????private?boolean?jcaptchaEbabled?=?true;//是否開啟驗證碼支持??????private?String?jcaptchaParam?=?"jcaptchaCode";//前臺提交的驗證碼參數(shù)名??????private?String?failureKeyAttribute?=?"shiroLoginFailure";?//驗證失敗后存儲到的屬性名??????public?void?setJcaptchaEbabled(boolean?jcaptchaEbabled)?{??????????this.jcaptchaEbabled?=?jcaptchaEbabled;??????}??????public?void?setJcaptchaParam(String?jcaptchaParam)?{??????????this.jcaptchaParam?=?jcaptchaParam;??????}??????public?void?setFailureKeyAttribute(String?failureKeyAttribute)?{??????????this.failureKeyAttribute?=?failureKeyAttribute;??????}??????protected?boolean?isAccessAllowed(ServletRequest?request,?ServletResponse?response,?Object?mappedValue)?throws?Exception?{??????????//1、設(shè)置驗證碼是否開啟屬性,頁面可以根據(jù)該屬性來決定是否顯示驗證碼??????????request.setAttribute("jcaptchaEbabled",?jcaptchaEbabled);????????????HttpServletRequest?httpServletRequest?=?WebUtils.toHttp(request);??????????//2、判斷驗證碼是否禁用?或不是表單提交(允許訪問)??????????if?(jcaptchaEbabled?==?false?||?!"post".equalsIgnoreCase(httpServletRequest.getMethod()))?{??????????????return?true;??????????}??????????//3、此時是表單提交,驗證驗證碼是否正確??????????return?JCaptcha.validateResponse(httpServletRequest,?httpServletRequest.getParameter(jcaptchaParam));??????}??????protected?boolean?onAccessDenied(ServletRequest?request,?ServletResponse?response)?throws?Exception?{??????????//如果驗證碼失敗了,存儲失敗key屬性??????????request.setAttribute(failureKeyAttribute,?"jCaptcha.error");??????????return?true;??????}??}??
?
七、MyFormAuthenticationFilter
用于驗證碼驗證的Shiro攔截器在用于身份認(rèn)證的攔截器之前運行;但是如果驗證碼驗證攔截器失敗了,就不需要進(jìn)行身份認(rèn)證攔截器流程了;所以需要修改下如FormAuthenticationFilter身份認(rèn)證攔截器,當(dāng)驗證碼驗證失敗時不再走身份認(rèn)證攔截器。?
Java代碼??
public?class?MyFormAuthenticationFilter?extends?FormAuthenticationFilter?{??????protected?boolean?onAccessDenied(ServletRequest?request,?ServletResponse?response,?Object?mappedValue)?throws?Exception?{??????????if(request.getAttribute(getFailureKeyAttribute())?!=?null)?{??????????????return?true;??????????}??????????return?super.onAccessDenied(request,?response,?mappedValue);??????}??}???
即如果之前已經(jīng)錯了,那直接跳過即可。
?
八、spring-config-shiro.xml? ? ???
Java代碼??
<!--?基于Form表單的身份驗證過濾器?-->??<bean?id="authcFilter"?????class="com.github.zhangkaitao.shiro.chapter22.jcaptcha.MyFormAuthenticationFilter">??????<property?name="usernameParam"?value="username"/>??????<property?name="passwordParam"?value="password"/>??????<property?name="rememberMeParam"?value="rememberMe"/>??????<property?name="failureKeyAttribute"?value="shiroLoginFailure"/>??</bean>??<bean?id="jCaptchaValidateFilter"?????class="com.github.zhangkaitao.shiro.chapter22.jcaptcha.JCaptchaValidateFilter">??????<property?name="jcaptchaEbabled"?value="true"/>??????<property?name="jcaptchaParam"?value="jcaptchaCode"/>??????<property?name="failureKeyAttribute"?value="shiroLoginFailure"/>??</bean>??<!--?Shiro的Web過濾器?-->??<bean?id="shiroFilter"?class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">??????<property?name="securityManager"?ref="securityManager"/>??????<property?name="loginUrl"?value="/login"/>??????<property?name="filters">??????????<util:map>??????????????<entry?key="authc"?value-ref="authcFilter"/>??????????????<entry?key="sysUser"?value-ref="sysUserFilter"/>??????????????<entry?key="jCaptchaValidate"?value-ref="jCaptchaValidateFilter"/>??????????</util:map>??????</property>??????<property?name="filterChainDefinitions">??????????<value>??????????????/static/**?=?anon??????????????/jcaptcha*?=?anon??????????????/login?=?jCaptchaValidate,authc??????????????/logout?=?logout??????????????/authenticated?=?authc??????????????/**?=?user,sysUser??????????</value>??????</property>??</bean>??
?
九、login.jsp登錄頁面
Java代碼??
<c:if?test="${jcaptchaEbabled}">??????驗證碼:??????<input?type="text"?name="jcaptchaCode">??<img?class="jcaptcha-btn?jcaptcha-img"???src="${pageContext.request.contextPath}/jcaptcha.jpg"?title="點擊更換驗證碼">??????<a?class="jcaptcha-btn"?href="javascript:;">換一張</a>??????<br/>??</c:if>???
根據(jù)jcaptchaEbabled來顯示驗證碼圖片。
?
十、測試
輸入http://localhost:8080/chapter22將重定向到登錄頁面;輸入正確的用戶名/密碼/驗證碼即可成功登錄,如果輸入錯誤的驗證碼,將顯示驗證碼錯誤頁面:?
??
?
示例源代碼:https://github.com/zhangkaitao/shiro-example;
?
?
?
十一、另附講解:
先說明錯誤原因:
用<a href="http://lib.csdn.net/base/javaee" class="replace_word" title="Java EE知識庫" target="_blank" >spring</a>安全攔截器進(jìn)行驗證碼的驗證的時候拋出異常。
throw new RuntimeException("captcha validation failed due to exception", cse);前臺提交數(shù)據(jù)后跳轉(zhuǎn)到如下方法:
?
?
package?com.davidstudio.gbp.core.security.jcaptcha;????import?org.acegisecurity.captcha.CaptchaServiceProxy;????import?org.apache.log4j.Logger;????import?com.octo.captcha.service.CaptchaService;??import?com.octo.captcha.service.CaptchaServiceException;????/**??*?調(diào)用CaptchaService類,完jcaptcha的驗證過程??*???*??*???*??*/??public?class?JCaptchaServiceProxyImpl?implements?CaptchaServiceProxy?{????????/**??????*?Logger?for?this?class??????*/??????private?static?final?Logger?logger?=?Logger.getLogger(JCaptchaServiceProxyImpl.class);????????private?CaptchaService?jcaptchaService;????????public?boolean?validateReponseForId(String?id,?Object?response)?{??????????if?(logger.isDebugEnabled())?{??????????????logger.debug("validating?captcha?response");??????????}?????????????try?{??????????????boolean?isHuman?=?false;????????????????????????????isHuman?=?jcaptchaService.validateResponseForID(id,?response).booleanValue();????????????????????????????if?(isHuman)?{??????????????????if?(logger.isDebugEnabled())?{??????????????????????logger.debug("captcha?passed");??????????????????}??????????????}?else?{??????????????????if?(logger.isDebugEnabled())?{??????????????????????logger.debug("captcha?failed");??????????????????}??????????????}??????????????return?isHuman;????????????????????????}?catch?(CaptchaServiceException?cse)?{??????????????//?fixes?known?bug?in?JCaptcha??????????????logger.warn("captcha?validation?failed?due?to?exception",?cse);??????????????throw?new?RuntimeException("captcha?validation?failed?due?to?exception",?cse);??????????}??????}????????public?void?setJcaptchaService(CaptchaService?jcaptchaService)?{??????????this.jcaptchaService?=?jcaptchaService;??????}??} ?
?
?
?
?
?
設(shè)置斷點debug改語句不能順利執(zhí)行?
?
jcaptchaService.validateResponseForID(id,?response).booleanValue(); ?
?
?
查了網(wǎng)上的資料,這個方法的作用是: 根據(jù)HttpSession的 sessionId進(jìn)行驗證碼的驗證,原理是這樣的,頁面生成的驗證碼是通過Spring中的配置生成的,查了一下配置:
?
<bean?id="security.filter.manager"?class="org.acegisecurity.util.FilterChainProxy">??????????<property?name="filterInvocationDefinitionSource">??????????????<value>??????????????????PATTERN_TYPE_APACHE_ANT??????????????????/**=security.filter.channel,security.filter.sessionIntegration,security.filter.logout,security.filter.thsso,security.filter.jcaptcha,security.filter.jcaptchachannel,security.filter.formAuth,security.filter.requestWrap,security.filter.exceptionTranslation,security.filter.filterInvocation??????????????</value>??????????</property>??????</bean>?
?
?
?
?
這是一個過濾器鏈,其中登錄的時候會進(jìn)行如下過濾操作,
?
security.filter.channel,security.filter.sessionIntegration,security.filter.logout,security.filter.thsso,security.filter.jcaptcha,security.filter.jcaptchachannel,security.filter.formAuth,security.filter.requestWrap,security.filter.exceptionTranslation,security.filter.filterInvocation
一般配置的順序不能變,因為這是這些配置定義了用戶登錄的一套認(rèn)證機(jī)制。
看了一下命名還算規(guī)范,其中涉及到驗證碼的過濾:security.filter.jcaptcha
查了一下這個驗證碼的引用配置:
?
<!--?jcaptacha過濾器?-->??????<bean?id="security.filter.jcaptcha"??????????class="org.acegisecurity.captcha.CaptchaValidationProcessingFilter">??????????<property?name="captchaService"?ref="security.captcha.serviceproxy"?/>??????????<property?name="captchaValidationParameter"?value="j_captcha_response"?/>??????</bean>??????<bean?id="security.captcha.serviceproxy"??????????class="com.davidstudio.gbp.core.security.jcaptcha.JCaptchaServiceProxyImpl">??????????<property?name="jcaptchaService"?ref="security.captcha.service"?/>??????</bean>??????<bean?id="security.captcha.service"??????????class="com.octo.captcha.service.image.DefaultManageableImageCaptchaService">??????????<constructor-arg?type="com.octo.captcha.service.captchastore.CaptchaStore"?index="0">??????????????<bean?class="com.octo.captcha.service.captchastore.FastHashMapCaptchaStore"?/>??????????</constructor-arg>??????????<constructor-arg?type="com.octo.captcha.engine.CaptchaEngine"?index="1">??????????????<bean?class="com.davidstudio.gbp.core.security.jcaptcha.CaptchaEngine"?/>??????????</constructor-arg>??????????<constructor-arg?index="2">??????????????<value>180</value>??????????</constructor-arg>??????????<constructor-arg?index="3">??????????????<value>100000</value>??????????</constructor-arg>??????????<constructor-arg?index="4">??????????????<value>75000</value>??????????</constructor-arg>??????</bean>??
?
?
?
通過bean配置反復(fù)引用。
?
剛開始以為SecurityContext沒有創(chuàng)建,查了一下配置也創(chuàng)建了:
?
?
<!--??session整合過濾器。自動將用戶身份信息存放在session里。?-->??<bean?id="security.filter.sessionIntegration"??????class="org.acegisecurity.context.HttpSessionContextIntegrationFilter">??????<property?name="context"?value="org.acegisecurity.captcha.CaptchaSecurityContextImpl"?/>??</bean>??
?
?
?
?copy
? 仔細(xì)看了一下這個方法的作用:
?
jcaptchaService.validateResponseForID(id,?response).booleanValue(); ?
?
?
?
id就是httpSession的Id,response是從頁面獲得的輸入的驗證碼,當(dāng)調(diào)用這個方法的時候,根據(jù)httpSession的id找到相應(yīng)的驗證碼,如果有sessionId并且sessionId對應(yīng)的驗證碼和輸入的驗證碼(這里就是response)一致的時候返回true,也就是用戶通過了驗證。
?
有一個疑問,驗證碼是怎么生成的?又怎么和httpSession進(jìn)行綁定的?其實這套理論是可行的,當(dāng)用戶第一次訪問頁面的時候會生成一個sessionId,頁面生成有驗證碼,關(guān)于驗證碼的生成,下面會進(jìn)行介紹。就是畫一個圖片以留的方式顯示到頁面而已。用戶訪問的時候有一個對應(yīng)的驗證碼和sessionId相對應(yīng)。
如果驗證碼不清楚,點擊換一張,因為瀏覽器沒有關(guān)閉,sessionId依然是那個sessionId,只需要更新生成的驗證碼的值即可,這樣就做到了一個sessionId和一個驗證碼進(jìn)行綁定了,這個過程在生成驗證碼的過程中就發(fā)生了。
如果用戶再次提交登錄信息,其中的sessionId沒有變,驗證碼是最新生成的驗證碼并且和sessionId進(jìn)行了綁定,這樣就可以調(diào)用:
?
?
jcaptchaService.validateResponseForID(id,?response).booleanValue();?這個條件進(jìn)行驗證碼的驗證了,當(dāng)然了驗證碼驗證前面還可以有很多過濾器認(rèn)證,比如說對用戶名和密碼的驗證等等。形成一套的鏈?zhǔn)秸J(rèn)證! ?
?
?
?
? ? ? 然而還有一個疑惑,這個sessionId是怎么和驗證碼進(jìn)行綁定的呢?又是怎樣進(jìn)行存儲的呢?
?我們看一下內(nèi)存:
調(diào)用這段代碼的時候內(nèi)存中有sessionId和response驗證碼的值:
下面是驗證碼生成的線程中內(nèi)存的狀態(tài):
由內(nèi)存的狀態(tài)可以看出和配置文件是一致的,首先調(diào)用了com.davidstudio.gbp.core.security.jcaptcha.JCaptchaServiceProxyImpl
這個代理實現(xiàn),這個代理實現(xiàn)類 又去調(diào)用com.octo.captcha.service.image.DefaultManageableImageCaptchaService
這個類才是生成驗證碼的類:查下spring這個類的源碼如下:
?
傳入的參數(shù)都有相應(yīng)的說明,其中這個類繼承了AbstractManageableImageCaptchaService ?
?
繼續(xù)深入到這個類中看個究竟:
?
這個類中果然有我們想要的方法:
?
? 相應(yīng)的通過store.getCaptcha(ID)通過這個ID獲得和這個sessionId匹配的驗證碼,再調(diào)用vilidateResponse方法進(jìn)行驗證,如果和輸入的驗證碼相同就驗證通過了。
?
驗證通過后就把這個sessionId刪除了,如果你再次登錄,輸入驗證碼的時候是同一個邏輯,之所以刪除了這個ID我想是有好處的:
? ? ? ? ? ?原因如下,如果不進(jìn)行刪除,隨著的登錄訪問用戶的過多,hashMap中的值會越來越多,這樣以后再進(jìn)行驗證的時候速度和效率都會受到印象,如果刪除了這個sessionId,這樣這個store中的hashMap只是存儲了當(dāng)前正在準(zhǔn)備登錄的sessionId和相應(yīng)的驗證碼!這樣效率就大大提高了,如果有10萬個人同時登錄,都不是問題!
? ? ? ?通過這個方法的調(diào)用我們就知道了sessionId是怎么和驗證碼綁定存儲在hashMap中的!讓我們進(jìn)入源碼驗證一下:
?
?
上面就是CaptchaStore接口的實現(xiàn)類MapCaptchaStore,其中定義了一個hashMap,通過storeCaptcha(String id,Captcha captcha)方法來存儲sessionId和captcha的鍵值對,這是進(jìn)入登錄頁面生成的時候調(diào)用的方法,當(dāng)進(jìn)行驗證的時候就需要hasCaptcha(String ID)方法和
?
但是我們是調(diào)用了
[java]?view plain?copy
MapCaptchaStore?的子類FastHashMapCaptchaStore來存儲信息的:同樣看FastHashMapCaptchaStore這個類:?? ?17???public?class?FastHashMapCaptchaStore?extends?MapCaptchaStore?{?????18???????public?FastHashMapCaptchaStore()?{?????19???????????this.store?=?new?FastHashMap();?????20???????}?????21???}?這就是這個類的全部了,再看一下FastHashMap類:???public?class?FastHashMap?extends?HashMap?{?????67????????68???????/**????69????????*?The?underlying?map?we?are?managing.????70????????*/?????71???????protected?HashMap?map?=?null;?????72????????73???????/**????74????????*?Are?we?currently?operating?in?"fast"?mode?????75????????*/?????76???????protected?boolean?fast?=?false;?????77????????78???????//?Constructors?????79???????//?----------------------------------------------------------------------?????80????????81???????/**????82????????*?Construct?an?empty?map.????83????????*/?????84???????public?FastHashMap()?{?????85???????????super();?????86???????????this.map?=?new?HashMap();?????87???????}?????這個類是HashMap的一個擴(kuò)展,里面有兩種方式操作,一種是快速的不同步,一種是同步的操作!???顯然FastHashMapCaptchaStore就是一個HashMap。驗證碼的實現(xiàn)在這個類中:?? ?18????*?Base?implementation?of?the?ImageCaptchaService.?????19????*?????20????*?@author?<a?href="mailto:mag@jcaptcha.net">Marc-Antoine?Garrigue</a>?????21????*?@version?1.0?????22????*/?????23???public?abstract?class?AbstractManageableImageCaptchaService?extends?AbstractManageableCaptchaService?????24???????????implements?ImageCaptchaService?{?????25????????26???????protected?AbstractManageableImageCaptchaService(CaptchaStore?captchaStore,?????27???????????????????????????????????????????????????????com.octo.captcha.engine.CaptchaEngine?captchaEngine,?????28???????????????????????????????????????????????????????int?minGuarantedStorageDelayInSeconds,?????29???????????????????????????????????????????????????????int?maxCaptchaStoreSize,?????30???????????????????????????????????????????????????????int?captchaStoreLoadBeforeGarbageCollection)?{?????31???????????super(captchaStore,?captchaEngine,?????32???????????????????minGuarantedStorageDelayInSeconds,?maxCaptchaStoreSize,?????33???????????????????captchaStoreLoadBeforeGarbageCollection);?????34???????}??? ?73???????protected?Object?getChallengeClone(Captcha?captcha)?{ ????74???????????BufferedImage?challenge?=?(BufferedImage)?captcha.getChallenge();?????75???????????BufferedImage?clone?=?new?BufferedImage(challenge.getWidth(),?challenge.getHeight(),?challenge.getType());?????76????????77???????????clone.getGraphics().drawImage(challenge,?0,?0,?clone.getWidth(),?clone.getHeight(),?null);?????78???????????clone.getGraphics().dispose();?????79????????80????????81???????????return?clone;?????82???????}?在這個類中,只是定義了一種,Captcha也是一種接口。??可以到內(nèi)存中看一看有木有那個hashMap<span?style="white-space:pre"><img?src="https://img-my.csdn.net/uploads/201211/23/1353676134_4969.png"?alt="">????</span>?內(nèi)存中清楚顯示了hashTable中的key和value,這樣就證明驗證碼生成成功。但是為什么每次驗證都是報錯呢?后來無奈看了看發(fā)送到?sessionId在hashMap中是否有,結(jié)果是不一樣,就是再hashMap中沒有,為什么?不是每一次在驗證碼生成的時候都把sessionId放進(jìn)去了嗎?為什么會不一樣呢??原因其實很簡單,就是當(dāng)點擊登陸的時候服務(wù)器又給分配了一個sessionId,這樣就和以前的sessionId不一樣了,在hashMap中就找不到對應(yīng)的驗證碼了。原則上講服務(wù)器在第一次訪問的時候會給用戶分配一個不重復(fù)的sessionId,如果服務(wù)器的session不超時就不會再給用戶分配sessionId了,減少給服務(wù)器的壓力,也帶來了友好的體驗。但是我的兩次sessiId為什么不一樣呢???后來通過fiddler2這個軟件(這個軟件好強(qiáng)大可以獲得發(fā)送到form表單的內(nèi)容,甚至可以修改),可以看到本地存儲的cookie,但是cookie是空的,就是nodata,汗啊,難怪每次都分配不同的sessionId,服務(wù)器怎么判斷每次提交過去的是同一個用戶呢??通過sessionId,服務(wù)器會在客戶端把sessionId寫在Cookie中,這樣用戶再次提交請求的時候,服務(wù)器如果在內(nèi)存中找到用戶cookie中的sessionId而且沒有超時,就不再重新分配sessionId,我看了下IE瀏覽器,cookie被禁止了,難怪每次都是一個新的sessionId,驗證碼就無法驗證。就報錯了。學(xué)習(xí)中應(yīng)該多去看源碼,分析源碼設(shè)計理念。
?
總結(jié)
以上是生活随笔為你收集整理的结合shiro 的图形验证码生成的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。