在做用户登录功能时,非常多时候都须要验证码支持,验证码的目的是为了防止机器人模拟真有用户登录而恶意訪问,如暴力破解用户password/恶意评论等。

眼下也有一些验证码比較简单,通过一些OCR工具就能够解析出来。另外另一些验证码比較复杂(一般通过如扭曲、加线条/噪点等干扰)防止OCR工具识别。可是在中国就是人多。机器干不了的能够交给人来完毕,所以在中国就有非常多打码平台。人工识别验证码;因此即使比較复杂的如填字、算数等类型的验证码还是能识别的。所以验证码也不是绝对可靠的,眼下比較可靠还是手机验证码,可是对于用户来说相对于验证码还是比較麻烦的。

对于验证码图片的生成。能够自己通过如Java提供的图像API自己去生成,也能够借助如JCaptcha这样的开源Java类库生成验证码图片;JCaptcha提供了常见的如扭曲、加噪点等干扰支持。本章代码基于《第十六章 综合实例》。

一、加入JCaptcha依赖

Java代码  
  1. </version>
  2. </version>
  3. <exclusions>
  4. <exclusion>
  5. <artifactId>servlet-api</artifactId>
  6. <groupId>javax.servlet</groupId>
  7. </exclusion>
  8. </exclusions>
  9. </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. , , );
  2. public static boolean validateResponse(
  3. HttpServletRequest request, String userCaptchaResponse) {
  4. if (request.getSession(false) == null) return false;
  5. boolean validated = false;
  6. try {
  7. String id = request.getSession().getId();
  8. validated =
  9. captchaService.validateResponseForID(id, userCaptchaResponse)
  10. .booleanValue();
  11. } catch (CaptchaServiceException e) {
  12. e.printStackTrace();
  13. }
  14. return validated;
  15. }
  16. public static boolean hasCaptcha(
  17. HttpServletRequest request, String userCaptchaResponse) {
  18. if (request.getSession(false) == null) return false;
  19. boolean validated = false;
  20. try {
  21. String id = request.getSession().getId();
  22. validated = captchaService.hasCapcha(id, userCaptchaResponse);
  23. } catch (CaptchaServiceException e) {
  24. e.printStackTrace();
  25. }
  26. return validated;
  27. }
  28. }

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. response.setDateHeader("Expires", 0L);
  4. response.setHeader("Cache-Control", "no-store, no-cache, must-revalidate");
  5. response.addHeader("Cache-Control", "post-check=0, pre-check=0");
  6. response.setHeader("Pragma", "no-cache");
  7. response.setContentType("image/jpeg");
  8. String id = request.getRequestedSessionId();
  9. BufferedImage bi = JCaptcha.captchaService.getImageChallengeForID(id);
  10. ServletOutputStream out = response.getOutputStream();
  11. ImageIO.write(bi, "jpg", out);
  12. try {
  13. out.flush();
  14. } finally {
  15. out.close();
  16. }
  17. }
  18. }

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. HttpServletRequest httpServletRequest = WebUtils.toHttp(request);
  18. //2、推断验证码是否禁用 或不是表单提交(同意訪问)
  19. if (jcaptchaEbabled == false || !"post".equalsIgnoreCase(httpServletRequest.getMethod())) {
  20. return true;
  21. }
  22. //3、此时是表单提交。验证验证码是否正确
  23. return JCaptcha.validateResponse(httpServletRequest, httpServletRequest.getParameter(jcaptchaParam));
  24. }
  25. protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
  26. //假设验证码失败了。存储失败key属性
  27. request.setAttribute(failureKeyAttribute, "jCaptcha.error");
  28. return true;
  29. }
  30. }

七、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

Shiro学习(22)集成验证码的更多相关文章

  1. Shiro学习笔记(5)——web集成

    Web集成 shiro配置文件shiroini 界面 webxml最关键 Servlet 測试 基于 Basic 的拦截器身份验证 Web集成 大多数情况.web项目都会集成spring.shiro在 ...

  2. Shiro学习(总结)

    声明:本文原文地址:http://www.iteye.com/blogs/subjects/shiro 感谢开涛提供的博文,让我学到了非常多.在这里由衷的感谢你,同一时候我强烈的推荐开涛的博文.他的博 ...

  3. Apache shiro学习总结

    Apache shiro集群实现 (一) shiro入门介绍 Apache shiro集群实现 (二) shiro 的INI配置 Apache shiro集群实现 (三)shiro身份认证(Shiro ...

  4. Shiro学习

    Shiro学习资源 Shiro官网,http://shiro.apache.org/index.html 学习网站链接,http://blog.java1234.com/blog/articles/4 ...

  5. Shiro 学习

    <转载于 凯涛 博客> Shiro目录 第一章  Shiro简介 第二章  身份验证 第三章  授权 第四章  INI配置 第五章  编码/加密 第六章  Realm及相关对象 第七章  ...

  6. SpringBoot中Shiro使用Pac4j集成CAS认证

    SpringBoot中Shiro使用Pac4j集成CAS认证 Pac4j 简介 Pac4j与Shiro,Spring Security一样都是权限框架,并且提供了OAuth - SAML - CAS ...

  7. SpringMVC+Apache Shiro+JPA(hibernate)案例教学(三)给Shiro登录验证加上验证码

    序: 给Shiro加入验证码,有多种方式,当然你也可以通过继承修改FormAuthenticationFilter类,通过Shiro去验证验证码.具体实现请百度: 应用Shiro到Web Applic ...

  8. 深度学习的集成方法——Ensemble Methods for Deep Learning Neural Networks

    本文主要参考Ensemble Methods for Deep Learning Neural Networks一文. 1. 前言 神经网络具有很高的方差,不易复现出结果,而且模型的结果对初始化参数异 ...

  9. Apache Shiro学习-2-Apache Shiro Web Support

     Apache Shiro Web Support  1. 配置 将 Shiro 整合到 Web 应用中的最简单方式是在 web.xml 的 Servlet ContextListener 和 Fil ...

  10. shiro学习笔记_0600_自定义realm实现授权

    博客shiro学习笔记_0400_自定义Realm实现身份认证 介绍了认证,这里介绍授权. 1,仅仅通过配置文件来指定权限不够灵活且不方便.在实际的应用中大多数情况下都是将用户信息,角色信息,权限信息 ...

随机推荐

  1. 清除浮动(float)的影响

    浮动会导致父元素塌陷如图: 解决办法: 父元素overflow:hidden,如图 末尾插入子元素clear,如图 为甚么,父元素overflow:hidden会解决塌陷问题? 来自知乎貘吃馍香的回答 ...

  2. mongo 3.4分片集群系列之七:配置数据库管理

    这个系列大致想跟大家分享以下篇章: 1.mongo 3.4分片集群系列之一:浅谈分片集群 2.mongo 3.4分片集群系列之二:搭建分片集群--哈希分片 3.mongo 3.4分片集群系列之三:搭建 ...

  3. post请求重定向到get请求问题

    springMVC默认重定向是get请求,我在方法注解中没有指定method是post还是get请求,这样就可以接收到post重定向来的请求,也可以接收到页面传来的get请求,如果要传参,可以使用mo ...

  4. 诊断:Goldengate OGG-01163 Bad column length

    故障现象: OGG- Bad column length () specified . 原因:源端修改了字段长度.虽然源端和目标端的长度已经通过DDL语句修改到一致,在extract进程未重启的情况下 ...

  5. enote笔记法的思考

    章节:enote笔记法的思考   why enote笔记法: key1)大脑喜欢颜色. 我们的大脑天生就喜欢颜色.对颜色很敏感,这是由我们人类过去的演化历程决定的. 你可以理解为,文字有了颜色,让这个 ...

  6. [Algorithm] 11. Linked List Cycle

    Description Given a linked list, determine if it has a cycle in it. To represent a cycle in the give ...

  7. UVA - 12589 Learning Vector(dp-01背包)

    题目: 思路: dp[j][h]表示选取了j个向量,且高度为h,利用01背包来解决问题. 没选当前的向量:dp[j][h] = dp[j][h]; 选了当前的向量:dp[j][h] = dp[j-1] ...

  8. centos7+VMware Workstation创建共享文件夹

    1.第一步设置宿主机共享文件夹路径 2.挂载VMware Tools,如下操作会将tools以光盘挂载点的方式进入到系统中. 3.centos7 挂载存有VMware Tools的光盘并进行安装 1) ...

  9. Vim常用快捷键--正常的学习曲线

    vim可能对于初学者不太友好,学习曲线有点陡,特此整理了较为平滑的学习曲线的学习快捷键的方式,包含最常用的快捷键,让初学者领悟vim的优点,想要进阶学习请查找其它更好的教程 正常模式:可以使用快捷键命 ...

  10. python3.3+selenium

    1.查看C:\Python33\Scripts下已经有了easy_install.exe; 2.从这里下载pip tar.gz,并解压到C盘,https://pypi.python.org/pypi/ ...