在做用户登录功能时,很多时候都需要验证码支持,验证码的目的是为了防止机器人模拟真实用户登录而恶意访问,如暴力破解用户密码/恶意评论等。目前也有一些验证码比较简单,通过一些OCR工具就可以解析出来;另外还有一些验证码比较复杂(一般通过如扭曲、加线条/噪点等干扰)防止OCR工具识别;但是在中国就是人多,机器干不了的可以交给人来完成,所以在中国就有很多打码平台,人工识别验证码;因此即使比较复杂的如填字、算数等类型的验证码还是能识别的。所以验证码也不是绝对可靠的,目前比较可靠还是手机验证码,但是对于用户来说相对于验证码还是比较麻烦的。

对于验证码图片的生成,可以自己通过如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(目前无法访问了),仿照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获取相应的验证码图片;另外需要设置响应内容不进行浏览器端缓存。

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";//前台提交的验证码参数名  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
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>   

根据jcaptchaEbabled来显示验证码图片。

十、测试
输入http://localhost:8080/chapter22将重定向到登录页面;输入正确的用户名/密码/验证码即可成功登录,如果输入错误的验证码,将显示验证码错误页面:

示例源代码:https://github.com/zhangkaitao/shiro-example
本文借鉴于:http://jinnianshilongnian.iteye.com/blog/2046041

Shiro学习(22)集成验证码相关推荐

  1. shiro学习(22):动态添加验证规则2

    工具idea 先看看数据库 shiro_role_permission 数据 shiro_user shiro_user_role 数据 目录结构 在pom.xml里面添加 <?xml vers ...

  2. shiro密码正确也会匹配错误_Shiro学习之——Shiro与Web集成

    Shiro与Web集成,主要是通过配置一个ShiroFilter拦截所有URL,其中ShiroFilter类似于如Strut2/SpringMVC这种web框架的前端控制器,是所有请求入口点,负责根据 ...

  3. shiro教程(4)-shiro与项目集成开发

    1 shiro与项目集成开发 这里我们主要以用户登录的例子来演示,先给出一个时序图: 点击打开链接(点击查看) 1.1 shiro与spring web项目整合 shiro与springweb项目整合 ...

  4. Shiro学习记录(详细)

    文章目录 Shiro学习记录 shiro核心组件 Spring Boot 整合 Shiro Shiro 整合 Thymeleaf Shiro学习记录 什么是 Shiro 官网:http://shiro ...

  5. Shiro学习笔记_02:shiro的认证+shiro的授权

    Shiro 学习笔记 本文基于B站UP主[编程不良人]视频教程[2020最新版Shiro教程,整合SpringBoot项目实战教程]进行整理记录,仅用于个人学习交流使用. 视频链接:https://w ...

  6. SpringBoot整合Shiro学习(上)

    SpringBoot整合Shiro(上) 基于[编程不良人]2020最新版Shiro教程,整合SpringBoot项目实战教程 哔哩哔哩链接:https://www.bilibili.com/vide ...

  7. Python学习22:Python之禅和PEP 8规范

    笔者:风起怨江南 出处:https://blog.csdn.net/JackMengJin 笔者原创,文章转载需注明,如果喜欢请点赞+关注,感谢支持! 导读:Python之禅和PEP 8规范,值得所有 ...

  8. shiro 学习(一)

    title: shiro学习(一) date: 2020-11-13 tags: spring springboot shiro categories: spring springboot shiro ...

  9. 利用深度学习(CNN)进行验证码(字母+数字)识别

    利用深度学习(CNN)进行验证码(字母+数字)识别_helen1313的专栏-CSDN博客 本文方法针对的验证码为定长验证码,不包含中文. 本文的思路是:1. 使用keras中预训练好的模型,在pyt ...

  10. 深度学习框架集成平台C++ Guide指南

    深度学习框架集成平台C++ Guide指南 这个指南详细地介绍了神经网络C++的API,并介绍了许多不同的方法来处理模型. 提示 所有框架运行时接口都是相同的,因此本指南适用于所有受支持框架(包括Te ...

最新文章

  1. 向上滚动tabBar隐藏向下显示
  2. 推荐:腾讯开源的词向量精简版本下载
  3. libxxx.so- text relocations问题的终极解决方案
  4. 软件工程实践2017第一次作业
  5. linux安装mongodb(设置非root用户和开机启动)
  6. USB应用开发笔记之一:STM32上实现USB主机读写U盘
  7. matlab 取绝对值最快的犯法,求助被积函数有绝对值号的问题。
  8. 利用rowid删除数据,提升性能
  9. 使用ln命令创建软引用(相对路径与绝对路径)
  10. matlab视频分辨率更改
  11. 阿里巴巴java开发手册-嵩山版 下载
  12. 敏捷 2016:行业分析研讨会
  13. 1.2 信息安全标准与规范
  14. 华为网吧服务器型号,网吧服务器价格
  15. WiFi认证—分析从连接WiFi到上网的全过程(一)
  16. stm32cubemx hal学习记录:JY901S串口
  17. 浏览器:免费小说的“下一站”
  18. 郭锡良古代汉语复习重点总结
  19. linux或者UC/OS
  20. 全国计算机等级考试python试题_全国计算机等级考试二级Python真题及解析(5)

热门文章

  1. springBoot thymeleaf 属性为空时报错:EL1007E
  2. 第三方支付平台:易宝支付
  3. java意图_任务型对话(一)—— NLU/SLU(意图识别和槽值填充)
  4. 【叮咚买菜】叮咚抢菜使用教程
  5. 字体的分类图示——对网页设计很有益的图
  6. Arduino - 继电器
  7. STM32定时器输入捕获,脉宽测量知识点
  8. cql oracle,Cassandra CQL中的Where和Order By子句
  9. AndroidIOS APP启动速度专项测试方法
  10. VRRP主备网关原理