JCaptcha做验证码遇到的问题引出的思考

JCaptcha用来做用户登录时期的验证码的,但是今天将开放的应用系统部署到生产环境的时候,遇到了问题,总是提示验证码不对。后台报出来下面的错误:

 1  com.octo.captcha.service.CaptchaServiceException: Invalid ID, could not validate unexisting or already validated captcha
 2         at com.octo.captcha.service.AbstractCaptchaService.validateResponseForID(AbstractCaptchaService.java:146)
 3         at com.octo.captcha.service.AbstractManageableCaptchaService.validateResponseForID(AbstractManageableCaptchaService.java:367)
 4         at com.tk.cms.core.shiro.JCaptcha.validateResponse(JCaptcha.java:19)
 5         at com.tk.cms.core.shiro.JCaptchaValidateFilter.isAccessAllowed(JCaptchaValidateFilter.java:44)
 6         at org.apache.shiro.web.filter.AccessControlFilter.onPreHandle(AccessControlFilter.java:162)
 7         at org.apache.shiro.web.filter.PathMatchingFilter.isFilterChainContinued(PathMatchingFilter.java:203)
 8         at org.apache.shiro.web.filter.PathMatchingFilter.preHandle(PathMatchingFilter.java:178)
 9         at org.apache.shiro.web.servlet.AdviceFilter.doFilterInternal(AdviceFilter.java:131)
10         at org.apache.shiro.web.servlet.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:125)
11         at org.apache.shiro.web.servlet.ProxiedFilterChain.doFilter(ProxiedFilterChain.java:66)
12         at org.apache.shiro.web.servlet.AbstractShiroFilter.executeChain(AbstractShiroFilter.java:449)
13         at org.apache.shiro.web.servlet.AbstractShiroFilter$1.call(AbstractShiroFilter.java:365)
14         at org.apache.shiro.subject.support.SubjectCallable.doCall(SubjectCallable.java:90)
15         at org.apache.shiro.subject.support.SubjectCallable.call(SubjectCallable.java:83)
16         at org.apache.shiro.subject.support.DelegatingSubject.execute(DelegatingSubject.java:383)
17         at org.apache.shiro.web.servlet.AbstractShiroFilter.doFilterInternal(AbstractShiroFilter.java:362)
18         at org.apache.shiro.web.servlet.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:125)
19         at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:343)
20         at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:260)
21         at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
22         at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
23         at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:88)
24         at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:106)
25         at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
26         at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
27         at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:220)
28         at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:122)
29         at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:505)
30         at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:170)
31         at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:103)
32         at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:956)
33         at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:116)
34         at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:423)
35         at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1079)
36         at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:625)
37         at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:316)
38         at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
39         at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
40         at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
41         at java.lang.Thread.run(Thread.java:745)

网上也有很多种方案,其中就说到过,如下代码处有问题(红色部分代码执行的不是时候):

 1     /**
 2      * Method to validate a response to the challenge corresponding to the given ticket and remove the coresponding
 3      * captcha from the store.
 4      *
 5      * @param ID the ticket provided by the buildCaptchaAndGetID method
 6      * @return true if the response is correct, false otherwise.
 7      * @throws CaptchaServiceException if the ticket is invalid
 8      */
 9     public Boolean validateResponseForID(String ID, Object response)
10             throws CaptchaServiceException {
11         if (!store.hasCaptcha(ID)) {
12             throw new CaptchaServiceException("Invalid ID, could not validate unexisting or already validated captcha");
13         } else {
14             Boolean valid = store.getCaptcha(ID).validateResponse(response);
15             store.removeCaptcha(ID);
16             return valid;
17         }
18     }

其实,我也仔细debug过,这个验证码错误的问题,其实是在store这个FastHashMap中通过sessionId为key去找是否存在这么一个jcaptcha实例,若没有就报exception了,最后就是验证码错误。 其实,这不是我应用中的问题。

针对这个问题,我一开始,怀疑是自己的代码写的出了问题,总在分析代码的流程,但是疑点是,我直接访问tomcat所在的机器,没有出现验证码的错误。但是一旦上到nginx的负载均衡环境,就遇到这个问题。想想,为何session不对???看看应用中的日志,sessionID和浏览器中cookie中的sessionId,总是不一样。。。这个就是问题的表象,根源在什么地方呢???

后来仔细看了看我们服务器的nginx的反向代理配置,发现upstream部分,没有配置负载均衡的策略,什么都没有配置呀。。。。我倒,什么都没有指定,那就是default的roundrobin啊,轮询啊。。。。我的神,这一个应用服务会有多少次http请求到达后端啊,每次轮询,那session对于后端的服务来说,岂不是没有地方hold了。。。不行。这个就是问题的根源。。。

我们的session没有专门的共享方案,所以,为了改动最小化,最好不改代码的情况下,我选择将upstream部分添加ip_hash策略,这样子能保证同一个IP请求的所有http都锁定在后端的一个服务上,这样子就不存在session丢失的问题了。

1  upstream cms {
2       server 10.130.14.51:8080;
3       server 10.130.14.53:8080;
4       ip_hash;
5     }

加上了上面的红色部分,问题解决!

思考:

1. 负载均衡环境下,session的管理是个问题,忽视这个问题,会造成登录都不可能完成。 遇到登录或者类似我这里验证码总是不对的情况,可以想想,是不是session的管理不到位!

2. 常见的session管理,比较靠谱的可控的方式有类似我这里的方案,基于ip_hash的策略,还有,就是专门的开发接口来管理session,不用tomcat容器的那一套。比如将session放在mysql里面,或者redis等中间件里面。

时间: 2024-10-11 11:43:33

JCaptcha做验证码遇到的问题引出的思考的相关文章

使用Tesseract-OCR 做验证码识别浅析

使用工具jTessBoxEditor-0.7(这个是在java平台下开发的,所以 它只支持java平台 ,在使用前应该先配置好java环境) tesseract 程序集(因为该程序集是在.net 2.0平台下 开发的,所以 只能支持到2.0 在使用时请注意(也可以自己去网上找别人用更高的版本编译好的)) tesseract-ocr-setup-3.01-1  使用开发语言.net 辅助工具 Visual Studio 至少能支持.net 2.0即可 首先,我们要找到自己需要做验证识别的验证码图片

ubuntu 安装(install) pwntcha[一个做"验证码识别"的开源程序]

一.安装 1. sudo apt-get install libsdl1.2-dev libsdl1.2debian sudo apt-get install libsdl1.2-dev(比较大,10M左右) sudo apt-get install libsdl-image1.2-dev sudo apt-get install libsdl-mixer1.2-dev sudo apt-get install libsdl-ttf2.0-dev sudo apt-get install lib

jcaptcha配置验证码

准备开始 首先导入jar包:jcaptcha-my-1.0 /** * web 常量 * @author lx * */ public abstract class Constants { /** 用户 session 的 cookie 名称*/ public static final String SESSION_ID = "JSESSIONID"; } 第一步:创建SessionProvider接口 cn.itcast.common.web.session.SessionProvi

WPF做验证码,小部分修改原作者内容

原文地址:http://www.cnblogs.com/tianguook/p/4142346.html 首先感谢aparche大牛的帖子,因为过两天可能要做个登录的页面,因此,需要用到验证码,从而看到了大神的这篇帖子,十分感谢. 产生验证码的类ValidCode public class ValidCode { public enum CodeType { Words, Numbers, Characters, Alphas } private const double PI = 3.1415

springmvc下使用kaptcha做验证码64534sybov

堂辕澳 厝恂镁犭 寥几句聊天便知谈吐不俗.右侧那身份古怪的年轻女子可真是长得灵气裴南苇身为胭脂 这座城池的建造可能称得上前无古人后无来者不但规模犹胜西北第一边城虎头城而且 人钟啜 由坦途官道岔入一条小道便是繁茂成林的芦苇荡王妃以往几年赏景千篇一律下车后就 有个归隐田园的老人在一个大雪纷飞的暮色中步履蹒跚不是前往那仅有娘俩扫墓却也 不光是沙场上建功立业的将领便是文官也都一律下意识抱拳还礼. 战怎么看都噱头十足近期已经挣了江湖人士无数斤的口水唾沫.但底层江湖侠士与绿林 謇查坟锴 锞霭行妹 眺育骠

angular中使用canvas画布做验证码

//填充画布,五位随机数 drawNumber(): void { this.clearCanvas(); let ctx: CanvasRenderingContext2D = this.myGraph.nativeElement.getContext("2d"); ctx.shadowBlur = 200; ctx.shadowColor = "black"; ctx.font = "italic 20px Verdana"; ctx.tex

设计表的时候,对于自增列做逻辑主键使用的一点思考

本文出处:http://www.cnblogs.com/wy123/p/7581380.html (保留出处并非什么原创作品权利,本人拙作还远远达不到,仅仅是为了链接到原文,因为后续对可能存在的一些错误进行修正或补充,无他) 关于自增列 自增列作为数据库的一个特性之一,在MSSQL和MySQL以及Oracle中都被支持.之前在网上发现一个类似的问题,是关于MySQL的:“为什么InnoDB表最好要有自增列做主键?”自增列作为一项特性,(可能)会应用到表的设计方面,不管是在那种数据库平台下.抛开具

虚拟机做服务器实现无人值守安装系统的思考和问题

这个问题我研究很久,目前的情况是这样的,让我慢慢道来. 一.整体拓扑 1.笔记本电脑宿主主机(xp系统),宿主主机上安装一个vm10的虚拟机. 2.虚拟机安装redhat6.0的linux系统,作为DHCP服务器.TFTP服务器.NFS服务器/HTTP服务器/FTP服务器的整体服务器. 3.客户机是另外一台没有任何系统的带有PXE功能的电脑,并且BIOS已经配置成为网卡启动. 4.是我简单画了一个图片,以便理解. 二.思考和问题 本人在一个独立的服务器上部署这些相关的服务器,可以顺利完成无人值守

从Python切片[::-1]可实现序列逆序引出的思考

初学Python,相较于之前所学的C/C++,其序列的索引和切片功能是第一个给人以幸福感的特性 既可以正向索引,0,1,2..., 也可以逆向索引,-1, -2, -3... 支持切片,其格式为[start : end: step],表示从start以step为步长直到end,但不包括end,start默认值为0,end默认值为到结束,step默认为1 然后,有一些初级小结论,比如:对于序列a = [1, 2, 3],  a[ : : -1]可实现逆序,即[3, 2, 1] 这里我产生了一个疑惑