<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??碼云GVP開源項目 12k star Uniapp+ElementUI 功能強大 支持多語言、二開方便! 廣告
                在做用戶登錄功能時,很多時候都需要驗證碼支持,驗證碼的目的是為了防止機器人模擬真實用戶登錄而惡意訪問,如暴力破解用戶密碼 / 惡意評論等。目前也有一些驗證碼比較簡單,通過一些 OCR 工具就可以解析出來;另外還有一些驗證碼比較復雜(一般通過如扭曲、加線條 / 噪點等干擾)防止 OCR 工具識別;但是在中國就是人多,機器干不了的可以交給人來完成,所以在中國就有很多打碼平臺,人工識別驗證碼;因此即使比較復雜的如填字、算數等類型的驗證碼還是能識別的。所以驗證碼也不是絕對可靠的,目前比較可靠還是手機驗證碼,但是對于用戶來說相對于驗證碼還是比較麻煩的。 對于驗證碼圖片的生成,可以自己通過如 Java 提供的圖像 API 自己去生成,也可以借助如 JCaptcha 這種開源 Java 類庫生成驗證碼圖片;JCaptcha 提供了常見的如扭曲、加噪點等干擾支持。本章代碼基于《第十六章 綜合實例》。 **一、添加 JCaptcha 依賴** ``` xml <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>(目前無法訪問了),仿照 JCaptcha2.0 編寫類似 GMail 驗證碼的樣式;具體請參考 com.github.zhangkaitao.shiro.chapter22.jcaptcha.GMailEngine。 **三、MyManageableImageCaptchaService** 提供了判斷倉庫中是否有相應的驗證碼存在。 ``` 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 工具類** 提供相應的 API 來驗證當前請求輸入的驗證碼是否正確。 ``` 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():驗證當前請求輸入的驗證碼否正確;并從 CaptchaService 中刪除已經生成的驗證碼; hasCaptcha():驗證當前請求輸入的驗證碼是否正確;但不從 CaptchaService 中刪除已經生成的驗證碼(比如 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 使用當前會話 ID 當作 key 獲取相應的驗證碼圖片;另外需要設置響應內容不進行瀏覽器端緩存。 ``` xml <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";//前臺提交的驗證碼參數名 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、設置驗證碼是否開啟屬性,頁面可以根據該屬性來決定是否顯示驗證碼 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 攔截器在用于身份認證的攔截器之前運行;但是如果驗證碼驗證攔截器失敗了,就不需要進行身份認證攔截器流程了;所以需要修改下如 FormAuthenticationFilter 身份認證攔截器,當驗證碼驗證失敗時不再走身份認證攔截器。 ``` 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); } } ``` 即如果之前已經錯了,那直接跳過即可。 **八、spring-config-shiro.xml** ``` xml <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> <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 登錄頁面** ``` xml <c:if test="${jcaptchaEbabled}"> 驗證碼: <input type="text" name="jcaptchaCode"> <img class="jcaptcha-btn jcaptcha-img" src="//img.w3cschool.cn/attachments/image/wk/shiro/jcaptcha.jpg" title="點擊更換驗證碼"> <a class="jcaptcha-btn" href="javascript:;">換一張</a> <br/> </c:if> ``` 根據 jcaptchaEbabled 來顯示驗證碼圖片。 **十、測試** 輸入 `http://localhost:8080/chapter22` 將重定向到登錄頁面;輸入正確的用戶名 / 密碼 / 驗證碼即可成功登錄,如果輸入錯誤的驗證碼,將顯示驗證碼錯誤頁面: ![](https://box.kancloud.cn/2f7c04c8a2cac34edaa214563185de7f_443x164.png)
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看