Shiro學習(22)集成驗證碼


在做用戶登錄功能時,非常多時候都須要驗證碼支持,驗證碼的目的是為了防止機器人模擬真有用戶登錄而惡意訪問,如暴力破解用戶password/惡意評論等。

眼下也有一些驗證碼比較簡單,通過一些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(), 18010000075000);  
  5.     public static boolean validateResponse(  
  6.         HttpServletRequest request, String userCaptchaResponse) {  
  7.         if (request.getSession(false) == nullreturn 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) == nullreturn 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將重定向到登錄頁面。輸入正確的username/password/驗證碼就可以成功登錄,假設輸入錯誤的驗證碼,將顯示驗證碼錯誤頁面: 


  

 

 

演示樣例源碼:https://github.com/zhangkaitao/shiro-example

本文借鑒於:http://jinnianshilongnian.iteye.com/blog/2046041


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM