結合shiro 的圖形驗證碼生成

前些天發現了一個巨牛的人工智能學習網站,通俗易懂,風趣幽默,忍不住分享一下給大家。點擊跳轉到教程。

在做用戶登錄功能時,很多時候都需要驗證碼支持,驗證碼的目的是為了防止機器人模擬真實用戶登錄而惡意訪問,如暴力破解用戶密碼/惡意評論等。目前也有一些驗證碼比較簡單,通過一些OCR工具就可以解析出來;另外還有一些驗證碼比較復雜(一般通過如扭曲、加線條/噪點等干擾)防止OCR工具識別;但是在中國就是人多,機器干不了的可以交給人來完成,所以在中國就有很多打碼平臺,人工識別驗證碼;因此即使比較復雜的如填字、算數等類型的驗證碼還是能識別的。所以驗證碼也不是絕對可靠的,目前比較可靠還是手機驗證碼,但是對于用戶來說相對于驗證碼還是比較麻煩的。

?

對于驗證碼圖片的生成,可以自己通過如Java提供的圖像API自己去生成,也可以借助如JCaptcha這種開源Java類庫生成驗證碼圖片;JCaptcha提供了常見的如扭曲、加噪點等干擾支持。

?

一、添加JCaptcha依賴?

Java代碼??收藏代碼
  1. <dependency>??
  2. ????<groupId>com.octo.captcha</groupId>??
  3. ????<artifactId>jcaptcha</artifactId>??
  4. ????<version>2.0-alpha-1</version>??
  5. </dependency>??
  6. <dependency>??
  7. ????<groupId>com.octo.captcha</groupId>??
  8. ????<artifactId>jcaptcha-integration-simple-servlet</artifactId>??
  9. ????<version>2.0-alpha-1</version>??
  10. ????<exclusions>??
  11. ????????<exclusion>??
  12. ????????????<artifactId>servlet-api</artifactId>??
  13. ????????????<groupId>javax.servlet</groupId>??
  14. ????????</exclusion>??
  15. ????</exclusions>??
  16. </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(目前無法訪問了),仿照JCaptcha2.0編寫類似GMail驗證碼的樣式;具體請參考com.github.zhangkaitao.shiro.chapter22.jcaptcha.GMailEngine。

?

三、MyManageableImageCaptchaService

提供了判斷倉庫中是否有相應的驗證碼存在。?

Java代碼??收藏代碼
  1. public?class?MyManageableImageCaptchaService?extends???
  2. ??DefaultManageableImageCaptchaService?{???
  3. ????public?MyManageableImageCaptchaService(??
  4. ??????com.octo.captcha.service.captchastore.CaptchaStore?captchaStore,????????
  5. ??????com.octo.captcha.engine.CaptchaEngine?captchaEngine,??
  6. ??????int?minGuarantedStorageDelayInSeconds,???
  7. ??????int?maxCaptchaStoreSize,???
  8. ??????int?captchaStoreLoadBeforeGarbageCollection)?{??
  9. ????????super(captchaStore,?captchaEngine,?minGuarantedStorageDelayInSeconds,???
  10. ????????????maxCaptchaStoreSize,?captchaStoreLoadBeforeGarbageCollection);??
  11. ????}??
  12. ????public?boolean?hasCapcha(String?id,?String?userCaptchaResponse)?{??
  13. ????????return?store.getCaptcha(id).validateResponse(userCaptchaResponse);??
  14. ????}??
  15. }??

??

?

四、JCaptcha工具類

提供相應的API來驗證當前請求輸入的驗證碼是否正確。??

Java代碼??收藏代碼
  1. public?class?JCaptcha?{??
  2. ????public?static?final?MyManageableImageCaptchaService?captchaService??
  3. ????????????=?new?MyManageableImageCaptchaService(new?FastHashMapCaptchaStore(),???
  4. ????????????????????????????new?GMailEngine(),?180,?100000,?75000);??
  5. ????public?static?boolean?validateResponse(??
  6. ????????HttpServletRequest?request,?String?userCaptchaResponse)?{??
  7. ????????if?(request.getSession(false)?==?null)?return?false;??
  8. ????????boolean?validated?=?false;??
  9. ????????try?{??
  10. ????????????String?id?=?request.getSession().getId();??
  11. ????????????validated?=???
  12. ????????????????captchaService.validateResponseForID(id,?userCaptchaResponse)??
  13. ????????????????????????????.booleanValue();??
  14. ????????}?catch?(CaptchaServiceException?e)?{??
  15. ????????????e.printStackTrace();??
  16. ????????}??
  17. ????????return?validated;??
  18. ????}???
  19. ????public?static?boolean?hasCaptcha(??
  20. ????????HttpServletRequest?request,?String?userCaptchaResponse)?{??
  21. ????????if?(request.getSession(false)?==?null)?return?false;??
  22. ????????boolean?validated?=?false;??
  23. ????????try?{??
  24. ????????????String?id?=?request.getSession().getId();??
  25. ????????????validated?=?captchaService.hasCapcha(id,?userCaptchaResponse);??
  26. ????????}?catch?(CaptchaServiceException?e)?{??
  27. ????????????e.printStackTrace();??
  28. ????????}??
  29. ????????return?validated;??
  30. ????}??
  31. }???

validateResponse():驗證當前請求輸入的驗證碼否正確;并從CaptchaService中刪除已經生成的驗證碼;

hasCaptcha():驗證當前請求輸入的驗證碼是否正確;但不從CaptchaService中刪除已經生成的驗證碼(比如Ajax驗證時可以使用,防止多次生成驗證碼);

?

五、JCaptchaFilter

用于生成驗證碼圖片的過濾器。??

Java代碼??收藏代碼
  1. public?class?JCaptchaFilter?extends?OncePerRequestFilter?{??
  2. ????protected?void?doFilterInternal(HttpServletRequest?request,?HttpServletResponse?response,?FilterChain?filterChain)?throws?ServletException,?IOException?{??
  3. ??
  4. ????????response.setDateHeader("Expires",?0L);??
  5. ????????response.setHeader("Cache-Control",?"no-store,?no-cache,?must-revalidate");??
  6. ????????response.addHeader("Cache-Control",?"post-check=0,?pre-check=0");??
  7. ????????response.setHeader("Pragma",?"no-cache");??
  8. ????????response.setContentType("image/jpeg");??
  9. ????????String?id?=?request.getRequestedSessionId();??
  10. ????????BufferedImage?bi?=?JCaptcha.captchaService.getImageChallengeForID(id);??
  11. ????????ServletOutputStream?out?=?response.getOutputStream();??
  12. ????????ImageIO.write(bi,?"jpg",?out);??
  13. ????????try?{??
  14. ????????????out.flush();??
  15. ????????}?finally?{??
  16. ????????????out.close();??
  17. ????????}??
  18. ????}??
  19. }???

CaptchaService使用當前會話ID當作key獲取相應的驗證碼圖片;另外需要設置響應內容不進行瀏覽器端緩存。?

?

Java代碼??收藏代碼
  1. <!--?驗證碼過濾器需要放到Shiro之后?因為Shiro將包裝HttpSession?如果不,可能造成兩次的sesison?id?不一樣?-->??
  2. <filter>??
  3. ??<filter-name>JCaptchaFilter</filter-name>??
  4. ??<filter-class>???
  5. ????com.github.zhangkaitao.shiro.chapter22.jcaptcha.JCaptchaFilter??
  6. ??</filter-class>??
  7. ??</filter>??
  8. ??<filter-mapping>??
  9. ????<filter-name>JCaptchaFilter</filter-name>??
  10. ????<url-pattern>/jcaptcha.jpg</url-pattern>??
  11. </filter-mapping>???

這樣就可以在頁面使用/jcaptcha.jpg地址顯示驗證碼圖片。

?

六、JCaptchaValidateFilter

用于驗證碼驗證的Shiro過濾器。??

Java代碼??收藏代碼
  1. public?class?JCaptchaValidateFilter?extends?AccessControlFilter?{??
  2. ????private?boolean?jcaptchaEbabled?=?true;//是否開啟驗證碼支持??
  3. ????private?String?jcaptchaParam?=?"jcaptchaCode";//前臺提交的驗證碼參數名??
  4. ????private?String?failureKeyAttribute?=?"shiroLoginFailure";?//驗證失敗后存儲到的屬性名??
  5. ????public?void?setJcaptchaEbabled(boolean?jcaptchaEbabled)?{??
  6. ????????this.jcaptchaEbabled?=?jcaptchaEbabled;??
  7. ????}??
  8. ????public?void?setJcaptchaParam(String?jcaptchaParam)?{??
  9. ????????this.jcaptchaParam?=?jcaptchaParam;??
  10. ????}??
  11. ????public?void?setFailureKeyAttribute(String?failureKeyAttribute)?{??
  12. ????????this.failureKeyAttribute?=?failureKeyAttribute;??
  13. ????}??
  14. ????protected?boolean?isAccessAllowed(ServletRequest?request,?ServletResponse?response,?Object?mappedValue)?throws?Exception?{??
  15. ????????//1、設置驗證碼是否開啟屬性,頁面可以根據該屬性來決定是否顯示驗證碼??
  16. ????????request.setAttribute("jcaptchaEbabled",?jcaptchaEbabled);??
  17. ??
  18. ????????HttpServletRequest?httpServletRequest?=?WebUtils.toHttp(request);??
  19. ????????//2、判斷驗證碼是否禁用?或不是表單提交(允許訪問)??
  20. ????????if?(jcaptchaEbabled?==?false?||?!"post".equalsIgnoreCase(httpServletRequest.getMethod()))?{??
  21. ????????????return?true;??
  22. ????????}??
  23. ????????//3、此時是表單提交,驗證驗證碼是否正確??
  24. ????????return?JCaptcha.validateResponse(httpServletRequest,?httpServletRequest.getParameter(jcaptchaParam));??
  25. ????}??
  26. ????protected?boolean?onAccessDenied(ServletRequest?request,?ServletResponse?response)?throws?Exception?{??
  27. ????????//如果驗證碼失敗了,存儲失敗key屬性??
  28. ????????request.setAttribute(failureKeyAttribute,?"jCaptcha.error");??
  29. ????????return?true;??
  30. ????}??
  31. }??

?

七、MyFormAuthenticationFilter

用于驗證碼驗證的Shiro攔截器在用于身份認證的攔截器之前運行;但是如果驗證碼驗證攔截器失敗了,就不需要進行身份認證攔截器流程了;所以需要修改下如FormAuthenticationFilter身份認證攔截器,當驗證碼驗證失敗時不再走身份認證攔截器。?

Java代碼??收藏代碼
  1. public?class?MyFormAuthenticationFilter?extends?FormAuthenticationFilter?{??
  2. ????protected?boolean?onAccessDenied(ServletRequest?request,?ServletResponse?response,?Object?mappedValue)?throws?Exception?{??
  3. ????????if(request.getAttribute(getFailureKeyAttribute())?!=?null)?{??
  4. ????????????return?true;??
  5. ????????}??
  6. ????????return?super.onAccessDenied(request,?response,?mappedValue);??
  7. ????}??
  8. }???

即如果之前已經錯了,那直接跳過即可。

?

八、spring-config-shiro.xml? ? ???

Java代碼??收藏代碼
  1. <!--?基于Form表單的身份驗證過濾器?-->??
  2. <bean?id="authcFilter"???
  3. ??class="com.github.zhangkaitao.shiro.chapter22.jcaptcha.MyFormAuthenticationFilter">??
  4. ????<property?name="usernameParam"?value="username"/>??
  5. ????<property?name="passwordParam"?value="password"/>??
  6. ????<property?name="rememberMeParam"?value="rememberMe"/>??
  7. ????<property?name="failureKeyAttribute"?value="shiroLoginFailure"/>??
  8. </bean>??
  9. <bean?id="jCaptchaValidateFilter"???
  10. ??class="com.github.zhangkaitao.shiro.chapter22.jcaptcha.JCaptchaValidateFilter">??
  11. ????<property?name="jcaptchaEbabled"?value="true"/>??
  12. ????<property?name="jcaptchaParam"?value="jcaptchaCode"/>??
  13. ????<property?name="failureKeyAttribute"?value="shiroLoginFailure"/>??
  14. </bean>??
  15. <!--?Shiro的Web過濾器?-->??
  16. <bean?id="shiroFilter"?class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">??
  17. ????<property?name="securityManager"?ref="securityManager"/>??
  18. ????<property?name="loginUrl"?value="/login"/>??
  19. ????<property?name="filters">??
  20. ????????<util:map>??
  21. ????????????<entry?key="authc"?value-ref="authcFilter"/>??
  22. ????????????<entry?key="sysUser"?value-ref="sysUserFilter"/>??
  23. ????????????<entry?key="jCaptchaValidate"?value-ref="jCaptchaValidateFilter"/>??
  24. ????????</util:map>??
  25. ????</property>??
  26. ????<property?name="filterChainDefinitions">??
  27. ????????<value>??
  28. ????????????/static/**?=?anon??
  29. ????????????/jcaptcha*?=?anon??
  30. ????????????/login?=?jCaptchaValidate,authc??
  31. ????????????/logout?=?logout??
  32. ????????????/authenticated?=?authc??
  33. ????????????/**?=?user,sysUser??
  34. ????????</value>??
  35. ????</property>??
  36. </bean>??

?

九、login.jsp登錄頁面

Java代碼??收藏代碼
  1. <c:if?test="${jcaptchaEbabled}">??
  2. ????驗證碼:??
  3. ????<input?type="text"?name="jcaptchaCode">??
  4. <img?class="jcaptcha-btn?jcaptcha-img"???
  5. src="${pageContext.request.contextPath}/jcaptcha.jpg"?title="點擊更換驗證碼">??
  6. ????<a?class="jcaptcha-btn"?href="javascript:;">換一張</a>??
  7. ????<br/>??
  8. </c:if>???

根據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>安全攔截器進行驗證碼的驗證的時候拋出異常。
throw new RuntimeException("captcha validation failed due to exception", cse);前臺提交數據后跳轉到如下方法:

?

?

  1. package?com.davidstudio.gbp.core.security.jcaptcha;??
  2. ??
  3. import?org.acegisecurity.captcha.CaptchaServiceProxy;??
  4. ??
  5. import?org.apache.log4j.Logger;??
  6. ??
  7. import?com.octo.captcha.service.CaptchaService;??
  8. import?com.octo.captcha.service.CaptchaServiceException;??
  9. ??
  10. /**?
  11. ?*?調用CaptchaService類,完jcaptcha的驗證過程?
  12. ?*??
  13. ?*?
  14. ?*??
  15. ?*?
  16. ?*/??
  17. public?class?JCaptchaServiceProxyImpl?implements?CaptchaServiceProxy?{??
  18. ??
  19. ????/**?
  20. ?????*?Logger?for?this?class?
  21. ?????*/??
  22. ????private?static?final?Logger?logger?=?Logger.getLogger(JCaptchaServiceProxyImpl.class);??
  23. ??
  24. ????private?CaptchaService?jcaptchaService;??
  25. ??
  26. ????public?boolean?validateReponseForId(String?id,?Object?response)?{??
  27. ????????if?(logger.isDebugEnabled())?{??
  28. ????????????logger.debug("validating?captcha?response");??
  29. ????????}??
  30. ???
  31. ????????try?{??
  32. ????????????boolean?isHuman?=?false;??
  33. ??????????????
  34. ????????????isHuman?=?jcaptchaService.validateResponseForID(id,?response).booleanValue();??
  35. ??????????????
  36. ????????????if?(isHuman)?{??
  37. ????????????????if?(logger.isDebugEnabled())?{??
  38. ????????????????????logger.debug("captcha?passed");??
  39. ????????????????}??
  40. ????????????}?else?{??
  41. ????????????????if?(logger.isDebugEnabled())?{??
  42. ????????????????????logger.debug("captcha?failed");??
  43. ????????????????}??
  44. ????????????}??
  45. ????????????return?isHuman;??
  46. ??????????????
  47. ????????}?catch?(CaptchaServiceException?cse)?{??
  48. ????????????//?fixes?known?bug?in?JCaptcha??
  49. ????????????logger.warn("captcha?validation?failed?due?to?exception",?cse);??
  50. ????????????throw?new?RuntimeException("captcha?validation?failed?due?to?exception",?cse);??
  51. ????????}??
  52. ????}??
  53. ??
  54. ????public?void?setJcaptchaService(CaptchaService?jcaptchaService)?{??
  55. ????????this.jcaptchaService?=?jcaptchaService;??
  56. ????}??
  57. } ?

?

?

?

?

?

設置斷點debug改語句不能順利執行?

?

  1. jcaptchaService.validateResponseForID(id,?response).booleanValue(); ?

?

?

查了網上的資料,這個方法的作用是: 根據HttpSession的 sessionId進行驗證碼的驗證,原理是這樣的,頁面生成的驗證碼是通過Spring中的配置生成的,查了一下配置:

?

  1. <bean?id="security.filter.manager"?class="org.acegisecurity.util.FilterChainProxy">??
  2. ????????<property?name="filterInvocationDefinitionSource">??
  3. ????????????<value>??
  4. ????????????????PATTERN_TYPE_APACHE_ANT??
  5. ????????????????/**=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??
  6. ????????????</value>??
  7. ????????</property>??
  8. ????</bean>?

?

?

?

?

這是一個過濾器鏈,其中登錄的時候會進行如下過濾操作,

?

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

一般配置的順序不能變,因為這是這些配置定義了用戶登錄的一套認證機制。

看了一下命名還算規范,其中涉及到驗證碼的過濾:security.filter.jcaptcha

查了一下這個驗證碼的引用配置:

?

  1. <!--?jcaptacha過濾器?-->??
  2. ????<bean?id="security.filter.jcaptcha"??
  3. ????????class="org.acegisecurity.captcha.CaptchaValidationProcessingFilter">??
  4. ????????<property?name="captchaService"?ref="security.captcha.serviceproxy"?/>??
  5. ????????<property?name="captchaValidationParameter"?value="j_captcha_response"?/>??
  6. ????</bean>??
  7. ????<bean?id="security.captcha.serviceproxy"??
  8. ????????class="com.davidstudio.gbp.core.security.jcaptcha.JCaptchaServiceProxyImpl">??
  9. ????????<property?name="jcaptchaService"?ref="security.captcha.service"?/>??
  10. ????</bean>??
  11. ????<bean?id="security.captcha.service"??
  12. ????????class="com.octo.captcha.service.image.DefaultManageableImageCaptchaService">??
  13. ????????<constructor-arg?type="com.octo.captcha.service.captchastore.CaptchaStore"?index="0">??
  14. ????????????<bean?class="com.octo.captcha.service.captchastore.FastHashMapCaptchaStore"?/>??
  15. ????????</constructor-arg>??
  16. ????????<constructor-arg?type="com.octo.captcha.engine.CaptchaEngine"?index="1">??
  17. ????????????<bean?class="com.davidstudio.gbp.core.security.jcaptcha.CaptchaEngine"?/>??
  18. ????????</constructor-arg>??
  19. ????????<constructor-arg?index="2">??
  20. ????????????<value>180</value>??
  21. ????????</constructor-arg>??
  22. ????????<constructor-arg?index="3">??
  23. ????????????<value>100000</value>??
  24. ????????</constructor-arg>??
  25. ????????<constructor-arg?index="4">??
  26. ????????????<value>75000</value>??
  27. ????????</constructor-arg>??
  28. ????</bean>??

?

?

?

通過bean配置反復引用。

?

剛開始以為SecurityContext沒有創建,查了一下配置也創建了:

?

?

  1. <!--??session整合過濾器。自動將用戶身份信息存放在session里。?-->??
  2. <bean?id="security.filter.sessionIntegration"??
  3. ????class="org.acegisecurity.context.HttpSessionContextIntegrationFilter">??
  4. ????<property?name="context"?value="org.acegisecurity.captcha.CaptchaSecurityContextImpl"?/>??
  5. </bean>??

?

?

?

?copy

? 仔細看了一下這個方法的作用:

?

  1. jcaptchaService.validateResponseForID(id,?response).booleanValue(); ?

?

?

?

id就是httpSession的Id,response是從頁面獲得的輸入的驗證碼,當調用這個方法的時候,根據httpSession的id找到相應的驗證碼,如果有sessionId并且sessionId對應的驗證碼和輸入的驗證碼(這里就是response)一致的時候返回true,也就是用戶通過了驗證。

?

有一個疑問,驗證碼是怎么生成的?又怎么和httpSession進行綁定的?其實這套理論是可行的,當用戶第一次訪問頁面的時候會生成一個sessionId,頁面生成有驗證碼,關于驗證碼的生成,下面會進行介紹。就是畫一個圖片以留的方式顯示到頁面而已。用戶訪問的時候有一個對應的驗證碼和sessionId相對應。

如果驗證碼不清楚,點擊換一張,因為瀏覽器沒有關閉,sessionId依然是那個sessionId,只需要更新生成的驗證碼的值即可,這樣就做到了一個sessionId和一個驗證碼進行綁定了,這個過程在生成驗證碼的過程中就發生了。

如果用戶再次提交登錄信息,其中的sessionId沒有變,驗證碼是最新生成的驗證碼并且和sessionId進行了綁定,這樣就可以調用:

?

?

  1. jcaptchaService.validateResponseForID(id,?response).booleanValue();?這個條件進行驗證碼的驗證了,當然了驗證碼驗證前面還可以有很多過濾器認證,比如說對用戶名和密碼的驗證等等。形成一套的鏈式認證! ?

?

?

?

? ? ? 然而還有一個疑惑,這個sessionId是怎么和驗證碼進行綁定的呢?又是怎樣進行存儲的呢?

?我們看一下內存:

調用這段代碼的時候內存中有sessionId和response驗證碼的值:

下面是驗證碼生成的線程中內存的狀態:

由內存的狀態可以看出和配置文件是一致的,首先調用了com.davidstudio.gbp.core.security.jcaptcha.JCaptchaServiceProxyImpl

這個代理實現,這個代理實現類 又去調用com.octo.captcha.service.image.DefaultManageableImageCaptchaService

這個類才是生成驗證碼的類:查下spring這個類的源碼如下:

?

傳入的參數都有相應的說明,其中這個類繼承了AbstractManageableImageCaptchaService ?

?

繼續深入到這個類中看個究竟:

?

這個類中果然有我們想要的方法:

?

? 相應的通過store.getCaptcha(ID)通過這個ID獲得和這個sessionId匹配的驗證碼,再調用vilidateResponse方法進行驗證,如果和輸入的驗證碼相同就驗證通過了。

?

驗證通過后就把這個sessionId刪除了,如果你再次登錄,輸入驗證碼的時候是同一個邏輯,之所以刪除了這個ID我想是有好處的:

? ? ? ? ? ?原因如下,如果不進行刪除,隨著的登錄訪問用戶的過多,hashMap中的值會越來越多,這樣以后再進行驗證的時候速度和效率都會受到印象,如果刪除了這個sessionId,這樣這個store中的hashMap只是存儲了當前正在準備登錄的sessionId和相應的驗證碼!這樣效率就大大提高了,如果有10萬個人同時登錄,都不是問題!

? ? ? ?通過這個方法的調用我們就知道了sessionId是怎么和驗證碼綁定存儲在hashMap中的!讓我們進入源碼驗證一下:

?

?

上面就是CaptchaStore接口的實現類MapCaptchaStore,其中定義了一個hashMap,通過storeCaptcha(String id,Captcha captcha)方法來存儲sessionId和captcha的鍵值對,這是進入登錄頁面生成的時候調用的方法,當進行驗證的時候就需要hasCaptcha(String ID)方法和

?

但是我們是調用了

[java]?view plain?copy
  1. MapCaptchaStore?的子類FastHashMapCaptchaStore來存儲信息的:同樣看FastHashMapCaptchaStore這個類:
  2. ?
  3. ? ?17???public?class?FastHashMapCaptchaStore?extends?MapCaptchaStore?{??
  4. ???18???????public?FastHashMapCaptchaStore()?{??
  5. ???19???????????this.store?=?new?FastHashMap();??
  6. ???20???????}??
  7. ???21???}
  8. ?
  9. 這就是這個類的全部了,再看一下FastHashMap類:??
  10. ?
  11. public?class?FastHashMap?extends?HashMap?{??
  12. ???67?????
  13. ???68???????/**?
  14. ???69????????*?The?underlying?map?we?are?managing.?
  15. ???70????????*/??
  16. ???71???????protected?HashMap?map?=?null;??
  17. ???72?????
  18. ???73???????/**?
  19. ???74????????*?Are?we?currently?operating?in?"fast"?mode??
  20. ???75????????*/??
  21. ???76???????protected?boolean?fast?=?false;??
  22. ???77?????
  23. ???78???????//?Constructors??
  24. ???79???????//?----------------------------------------------------------------------??
  25. ???80?????
  26. ???81???????/**?
  27. ???82????????*?Construct?an?empty?map.?
  28. ???83????????*/??
  29. ???84???????public?FastHashMap()?{??
  30. ???85???????????super();??
  31. ???86???????????this.map?=?new?HashMap();??
  32. ???87???????}??
  33. ???
  34. 這個類是HashMap的一個擴展,里面有兩種方式操作,一種是快速的不同步,一種是同步的操作!??
  35. ?
  36. 顯然FastHashMapCaptchaStore就是一個HashMap。驗證碼的實現在這個類中:
  37. ?
  38. ? ?18????*?Base?implementation?of?the?ImageCaptchaService.??
  39. ???19????*??
  40. ???20????*?@author?<a?href="mailto:mag@jcaptcha.net">Marc-Antoine?Garrigue</a>??
  41. ???21????*?@version?1.0??
  42. ???22????*/??
  43. ???23???public?abstract?class?AbstractManageableImageCaptchaService?extends?AbstractManageableCaptchaService??
  44. ???24???????????implements?ImageCaptchaService?{??
  45. ???25?????
  46. ???26???????protected?AbstractManageableImageCaptchaService(CaptchaStore?captchaStore,??
  47. ???27???????????????????????????????????????????????????????com.octo.captcha.engine.CaptchaEngine?captchaEngine,??
  48. ???28???????????????????????????????????????????????????????int?minGuarantedStorageDelayInSeconds,??
  49. ???29???????????????????????????????????????????????????????int?maxCaptchaStoreSize,??
  50. ???30???????????????????????????????????????????????????????int?captchaStoreLoadBeforeGarbageCollection)?{??
  51. ???31???????????super(captchaStore,?captchaEngine,??
  52. ???32???????????????????minGuarantedStorageDelayInSeconds,?maxCaptchaStoreSize,??
  53. ???33???????????????????captchaStoreLoadBeforeGarbageCollection);??
  54. ???34???????}
  55. ?
  56. ?
  57. ? ?73???????protected?Object?getChallengeClone(Captcha?captcha)?{ ?
  58. ???74???????????BufferedImage?challenge?=?(BufferedImage)?captcha.getChallenge();??
  59. ???75???????????BufferedImage?clone?=?new?BufferedImage(challenge.getWidth(),?challenge.getHeight(),?challenge.getType());??
  60. ???76?????
  61. ???77???????????clone.getGraphics().drawImage(challenge,?0,?0,?clone.getWidth(),?clone.getHeight(),?null);??
  62. ???78???????????clone.getGraphics().dispose();??
  63. ???79?????
  64. ???80?????
  65. ???81???????????return?clone;??
  66. ???82???????}
  67. ?
  68. 在這個類中,只是定義了一種,Captcha也是一種接口。??
  69. 可以到內存中看一看有木有那個hashMap
  70. <span?style="white-space:pre"><img?src="https://img-my.csdn.net/uploads/201211/23/1353676134_4969.png"?alt="">????</span>
  71. ?
  72. 內存中清楚顯示了hashTable中的key和value,這樣就證明驗證碼生成成功。但是為什么每次驗證都是報錯呢?
  73. 后來無奈看了看發送到?sessionId在hashMap中是否有,結果是不一樣,就是再hashMap中沒有,為什么?不是每一次在驗證碼生成的時候都把sessionId放進去了嗎?為什么會不一樣呢?
  74. ?
  75. 原因其實很簡單,就是當點擊登陸的時候服務器又給分配了一個sessionId,這樣就和以前的sessionId不一樣了,在hashMap中就找不到對應的驗證碼了。
  76. 原則上講服務器在第一次訪問的時候會給用戶分配一個不重復的sessionId,如果服務器的session不超時就不會再給用戶分配sessionId了,減少給服務器的壓力,也帶來了友好的體驗。但是我的兩次sessiId為什么不一樣呢?
  77. ?
  78. ?后來通過fiddler2這個軟件(這個軟件好強大可以獲得發送到form表單的內容,甚至可以修改),可以看到本地存儲的cookie,但是cookie是空的,就是nodata,汗啊,難怪每次都分配不同的sessionId,服務器怎么判斷每次提交過去的是同一個用戶呢?
  79. ?
  80. 通過sessionId,服務器會在客戶端把sessionId寫在Cookie中,這樣用戶再次提交請求的時候,服務器如果在內存中找到用戶cookie中的sessionId而且沒有超時,就不再重新分配sessionId,我看了下IE瀏覽器,cookie被禁止了,難怪每次都是一個新的sessionId,驗證碼就無法驗證。就報錯了。
  81. 學習中應該多去看源碼,分析源碼設計理念。

?

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/452109.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/452109.shtml
英文地址,請注明出處:http://en.pswp.cn/news/452109.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

利用C語言實現計算機圖像處理的方法

1&#xff0e;圖像平移 圖像平移只是改變圖像在屏幕上的位置&#xff0c;圖像本身并不發生變化。假設原圖像區域左上角坐標為(x0, y0)&#xff0c;右下角坐標為(x1, y1)&#xff0c;將圖像分別沿x和y軸平移dx和dy&#xff0c;則新圖像的左上角坐標為(x0 &#xff0b; dx, y0 &a…

E24- please install the following Perl modules before executing ./mysql_install_db

2019獨角獸企業重金招聘Python工程師標準>>> [roott-cet7 scripts]# ./mysql_install_db --basedir/usr/local/mysql/ --datadir/app/data/ --usermysql FATAL ERROR: please install the following Perl modules before executing ./mysql_install_db: Data::Dumpe…

SpringMVC異常報406 (Not Acceptable)的解決辦法

使用SpsringMVC&#xff0c;使用restEasy調試&#xff0c;controller請求設置如下&#xff1a; Java代碼 RequestMapping(value"/list",methodRequestMethod.GET,producesMediaType.APPLICATION_JSON_VALUE) ResponseBody public List<EditTimeout> list()…

Diango博客--25.使用Coverage統計測試覆蓋率

文章目錄1. 前言2. 安裝 Coverage3. 簡單配置 Coverage4. 運行 Coverage5. 完善 Coverage 配置6. 生成 HTML 報告7. 完善單元測試1. 前言 我們完成了對 blog 應用和 comment 應用這兩個核心 app 的測試。現在我們想知道的是究竟測試效果怎么樣呢&#xff1f;測試充分嗎&#x…

面向對象語言的優點

1.一致的表達方法 從前面章節的講述中可以知道&#xff0c;面向對象開發基于不隨時間變化的、一致的表示方法。這種表示方法應該從問題域到OOA&#xff0c;從OOA到OOD&#xff0c;最后從OOD到面向對象編程(OOP)&#xff0c;始終穩定不變。 一致的表示方法&#xff1a; 既有利…

最好的英文詞典

辭典對于學外語的作用&#xff0c;怎么強調也不過分。經常接觸英語的人都知道&#xff0c;遇到生詞不可怕&#xff0c;可怕的是遇到認識的單詞&#xff0c;又不明白這句話什么意思。這個時候&#xff0c;辭典的作用就發揮出來了。 今天一位朋友問我一句英文的意思&#xff0c;…

oracle用戶創建及權限設置

權限&#xff1a; create session create table unlimited tablespace connect resource dba 例&#xff1a; #sqlplus /nolog SQL> conn / as sysdba; SQL>create user username identified by password SQL> grant dba to username; SQL> conn username/password…

Android動畫之逐幀動畫(FrameAnimation)詳解

今天我們就來學習逐幀動畫,廢話少說直接上效果圖如下: 幀動畫的實現方式有兩種&#xff1a; 一、在res/drawable文件夾下新建animation-list的XML實現幀動畫 1、首先在res/drawable文件夾下添加img00-img24共25張圖片 2、新建frame_anim.xml [html] view plaincopy <?xml v…

ajax-簡單參數方法實現陰影效果

注&#xff1a; 簡單參數 &#xff08;按照參數的數量和位置傳遞參數&#xff09; 使用時按照位置、數量傳遞 shadow.js函數//簡單參數實現方式/** slices:陰影* opacity:透明度* zIndex:層級* */jQuery.fn.shadow_simple function (slices,opacity,zIndex) { //獲取到每個…

第一二三范式的簡單理解

第一范式&#xff08;無重復的列&#xff09; 定義&#xff1a;數據庫表的每一列都是不可分割的原子數據項&#xff0c;而不能是集合&#xff0c;數組&#xff0c;記錄等非原子數據項。如果實體中的某個屬性有多個值時&#xff0c;必須拆分為不同的屬性 通俗解釋&#xff1a;一…

網絡爬蟲--1.通用爬蟲和聚焦爬蟲

文章目錄一.前言二.通用爬蟲1.工作原理2.通用爬蟲的局限性三.聚焦爬蟲一.前言 根據使用場景&#xff0c;網絡爬蟲可分為 通用爬蟲 和 聚焦爬蟲 兩種。 其中通用網絡爬蟲是捜索引擎抓取系統&#xff08;Baidu、Google、Yahoo等&#xff09;的重要組成部分。主要目的是將互聯網…

敏捷教練的工具箱

學習并不是簡簡單單的閱讀和瀏覽&#xff0c;而是一個積累的過程&#xff0c;一個通過持續的學習&#xff0c;對自己的知識體系不斷豐富、索引的過程。接下來我會從四個方面入手分享我的經驗。 高質量的信息源和高效的學習 Google是一個很好的工具&#xff0c;通過它&#x…

log4j教程

詳細的Log4j使用教程 轉載 2016年08月19日 14:44:49 5072 日志是應用軟件中不可缺少的部分&#xff0c;Apache的開源項目log4j是一個功能強大的日志組件,提供方便的日志記錄。在apache網站&#xff1a;jakarta.apache.org/log4j 可以免費下載到Log4j最新版本的軟件包。…

BC范式介紹

設關系模式R<U&#xff0c;F>∈1NF&#xff0c;如果對于R的每個函數依賴X→Y&#xff0c;若Y不屬于X&#xff0c;則X必含有候選碼&#xff0c;那么R∈BCNF。 即為&#xff1a;對于關系模式R&#xff0c;若 R為第一范式&#xff0c;且每個屬性都不部分依賴于候選鍵也不傳遞…

com.jhlabs:imaging:jar:01012005 所在倉庫+captcha驗證碼maven依賴

前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊跳轉到教程。 <repositories> <repository> <id>atlassian</id> <name>atlassian</name&g…

python 發送郵件的兩種方式【終極篇】

python 發送郵件的兩種方式【終極篇】 一&#xff0c;利用python自帶的庫 smtplib簡單高效 from email.mime.multipart import MIMEMultipart from email.mime.text import MIMEText from email.header import Header import smtplib from django.conf import settingsmail_hos…

網絡爬蟲--2.HTTP和HTTPS

文章目錄一.簡介二.HTTP的請求與響應三.客戶端HTTP請求1.格式2.請求方法四.常用的請求報頭1.Host (主機和端口號)2.Connection (鏈接類型)3.Upgrade-Insecure-Requests (升級為HTTPS請求)4. User-Agent (瀏覽器名稱)5. Accept (傳輸文件類型)6.Referer (頁面跳轉處)7.Accept-En…

解決win7的outlook打不開的問題

outlook打不開&#xff0c;一直顯示正在處理 解決方法&#xff1a; 1. 按住Ctrl,雙擊打開組件&#xff0c;會提示是否進入安全模式&#xff0c; 進入安全模式 2. 單擊Outlook中的文件-選項-加載項- 左下角的“COM加載項“ 旁邊的“轉到”&#xff0c;將所有加載項前面的勾都去掉…

IBM王陽:軟件是凝聚創新力的最佳平臺

導讀&#xff1a;在IBM全球副總裁兼IBM中國開發中心總經理王陽博士看來&#xff0c;IBM百年不衰的根本原因在于將創新力凝結成軟件然后進行合適的傳播&#xff0c;其間最重要的是成功打造出了一個吸引人才、培養研發人才并激發出人才創新力的環境和氛圍。而保持創新領導力的關鍵…

數據庫的規范化

在關系數據庫中&#xff0c;對關系模式的基本要求是滿足第一范式。 規范化程度過低的關系不一定能夠很好地描述現實世界 可能存在插入異常、刪除異常、修改復雜、數據冗余等問題 解決方法就是對其進行規范化&#xff0c;轉換成高級范式 一個低一級范式的關系模式&#xff0c;通…