Shiro学习(22)集成验证码
在做用户登录功能时,非常多时候都须要验证码支持,验证码的目的是为了防止机器人模拟真有用户登录而恶意訪问,如暴力破解用户password/恶意评论等。
眼下也有一些验证码比較简单,通过一些OCR工具就能够解析出来。另外另一些验证码比較复杂(一般通过如扭曲、加线条/噪点等干扰)防止OCR工具识别。可是在中国就是人多。机器干不了的能够交给人来完毕,所以在中国就有非常多打码平台。人工识别验证码;因此即使比較复杂的如填字、算数等类型的验证码还是能识别的。所以验证码也不是绝对可靠的,眼下比較可靠还是手机验证码,可是对于用户来说相对于验证码还是比較麻烦的。
对于验证码图片的生成。能够自己通过如Java提供的图像API自己去生成,也能够借助如JCaptcha这样的开源Java类库生成验证码图片;JCaptcha提供了常见的如扭曲、加噪点等干扰支持。本章代码基于《第十六章 综合实例》。
一、加入JCaptcha依赖
- </version>
- </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
提供了推断仓库中是否有对应的验证码存在。
- 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来验证当前请求输入的验证码是否正确。
- , , );
- 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
用于生成验证码图片的过滤器。
- 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获取对应的验证码图片;另外须要设置响应内容不进行浏览器端缓存。
- <!-- 验证码过滤器须要放到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过滤器。
- 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身份认证拦截器,当验证码验证失败时不再走身份认证拦截器。
- 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
- <!-- 基于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登录页面
- <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将重定向到登录页面。输入正确的username/password/验证码就可以成功登录,假设输入错误的验证码,将显示验证码错误页面:

演示样例源码:https://github.com/zhangkaitao/shiro-example
本文借鉴于:http://jinnianshilongnian.iteye.com/blog/2046041
Shiro学习(22)集成验证码的更多相关文章
- Shiro学习笔记(5)——web集成
Web集成 shiro配置文件shiroini 界面 webxml最关键 Servlet 測试 基于 Basic 的拦截器身份验证 Web集成 大多数情况.web项目都会集成spring.shiro在 ...
- Shiro学习(总结)
声明:本文原文地址:http://www.iteye.com/blogs/subjects/shiro 感谢开涛提供的博文,让我学到了非常多.在这里由衷的感谢你,同一时候我强烈的推荐开涛的博文.他的博 ...
- Apache shiro学习总结
Apache shiro集群实现 (一) shiro入门介绍 Apache shiro集群实现 (二) shiro 的INI配置 Apache shiro集群实现 (三)shiro身份认证(Shiro ...
- Shiro学习
Shiro学习资源 Shiro官网,http://shiro.apache.org/index.html 学习网站链接,http://blog.java1234.com/blog/articles/4 ...
- Shiro 学习
<转载于 凯涛 博客> Shiro目录 第一章 Shiro简介 第二章 身份验证 第三章 授权 第四章 INI配置 第五章 编码/加密 第六章 Realm及相关对象 第七章 ...
- SpringBoot中Shiro使用Pac4j集成CAS认证
SpringBoot中Shiro使用Pac4j集成CAS认证 Pac4j 简介 Pac4j与Shiro,Spring Security一样都是权限框架,并且提供了OAuth - SAML - CAS ...
- SpringMVC+Apache Shiro+JPA(hibernate)案例教学(三)给Shiro登录验证加上验证码
序: 给Shiro加入验证码,有多种方式,当然你也可以通过继承修改FormAuthenticationFilter类,通过Shiro去验证验证码.具体实现请百度: 应用Shiro到Web Applic ...
- 深度学习的集成方法——Ensemble Methods for Deep Learning Neural Networks
本文主要参考Ensemble Methods for Deep Learning Neural Networks一文. 1. 前言 神经网络具有很高的方差,不易复现出结果,而且模型的结果对初始化参数异 ...
- Apache Shiro学习-2-Apache Shiro Web Support
Apache Shiro Web Support 1. 配置 将 Shiro 整合到 Web 应用中的最简单方式是在 web.xml 的 Servlet ContextListener 和 Fil ...
- shiro学习笔记_0600_自定义realm实现授权
博客shiro学习笔记_0400_自定义Realm实现身份认证 介绍了认证,这里介绍授权. 1,仅仅通过配置文件来指定权限不够灵活且不方便.在实际的应用中大多数情况下都是将用户信息,角色信息,权限信息 ...
随机推荐
- opencv总结
2018-02-2623:59:02 唉,这软件我很烦躁,今天又搞了好几遍,出错提示的时候总是出问题! 而且,无论什么错误,都是提示一堆乱码! 定义ROI区域有两种方法,第一种是使用cv:Rect.顾 ...
- Ubantu下 docker的安装
另一种方法 1.切换到root权限或者用sudo 2.升级source列表并保证https和ca证书成功安装 # apt-get update # apt-get install apt-transp ...
- python 正则表达式操作
正则表达式操作 本模块提供了类似于Perl的正则表达式匹配操作.要匹配的模式和字符串可以是Unicode字符串以及8位字符串. 正则表达式使用反斜杠字符('\')来表示特殊的形式或者来允许使用特殊的 ...
- PHP开发心得四
1,php返回给html页面的Json数据不能含有回车符 某次用php编写查询数据库数据,以json格式返回给前端页面js文件,js文件以angularJS的函数调用处理的方式进行数据显示,但数据返回 ...
- Hive扩展功能(二)--HWI接口
软件环境: linux系统: CentOS6.7 Hadoop版本: 2.6.5 zookeeper版本: 3.4.8 主机配置: 一共m1, m2, m3这五部机, 每部主机的用户名都为centos ...
- 关于.Net的强名称(Strong Name)
下面是我在CSDN上发表的<关于.Net的强名称(Strong Name)>,转载于此. 关于.Net的强名称(Strong Name)
- 15年第六届蓝桥杯第七题_(string)
手链样式 小明有3颗红珊瑚,4颗白珊瑚,5颗黄玛瑙.他想用它们串成一圈作为手链,送给女朋友.现在小明想知道:如果考虑手链可以随意转动或翻转,一共可以有多少不同的组合样式呢? 请你提交该整数.不要填写任 ...
- centos右上角wired图标消失有效解决方案
最近在学习Linux配置nginx时,左上角的wired图标突然没了,很神奇.然后在网上按着很多博客说的去改,都没用,最后终于根据下面参考博客内的方案解决了问题,嘿嘿. mv /var/lib/Net ...
- js获取微信版本号的方法
微信现在几乎已经普遍在使用,但是微信版本一直在更新,不同的版本有不同的功能,于是了解微信的版本号很重要,以便自己及时更新体验新的功能.那么如何用js获取微信版本号呢? 一.判断微信版本号 ? 1 2 ...
- Vue指令6:v-show
根据表达式的真假值来渲染元素 用法大致一样: <h1 v-show="ok">Hello!</h1> 不同的是带有 v-show 的元素始终会被渲染并保留在 ...