日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

结合shiro 的图形验证码生成

發(fā)布時間:2023/12/18 编程问答 24 豆豆
生活随笔 收集整理的這篇文章主要介紹了 结合shiro 的图形验证码生成 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

前些天發(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)容還不錯,歡迎將生活随笔推薦給好友。