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

  com.octo.captcha.service.CaptchaServiceException: Invalid ID, could not validate unexisting or already validated captcha
at com.octo.captcha.service.AbstractCaptchaService.validateResponseForID(AbstractCaptchaService.java:)
at com.octo.captcha.service.AbstractManageableCaptchaService.validateResponseForID(AbstractManageableCaptchaService.java:)
at com.tk.cms.core.shiro.JCaptcha.validateResponse(JCaptcha.java:)
at com.tk.cms.core.shiro.JCaptchaValidateFilter.isAccessAllowed(JCaptchaValidateFilter.java:)
at org.apache.shiro.web.filter.AccessControlFilter.onPreHandle(AccessControlFilter.java:)
at org.apache.shiro.web.filter.PathMatchingFilter.isFilterChainContinued(PathMatchingFilter.java:)
at org.apache.shiro.web.filter.PathMatchingFilter.preHandle(PathMatchingFilter.java:)
at org.apache.shiro.web.servlet.AdviceFilter.doFilterInternal(AdviceFilter.java:)
at org.apache.shiro.web.servlet.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:)
at org.apache.shiro.web.servlet.ProxiedFilterChain.doFilter(ProxiedFilterChain.java:)
at org.apache.shiro.web.servlet.AbstractShiroFilter.executeChain(AbstractShiroFilter.java:)
at org.apache.shiro.web.servlet.AbstractShiroFilter$.call(AbstractShiroFilter.java:)
at org.apache.shiro.subject.support.SubjectCallable.doCall(SubjectCallable.java:)
at org.apache.shiro.subject.support.SubjectCallable.call(SubjectCallable.java:)
at org.apache.shiro.subject.support.DelegatingSubject.execute(DelegatingSubject.java:)
at org.apache.shiro.web.servlet.AbstractShiroFilter.doFilterInternal(AbstractShiroFilter.java:)
at org.apache.shiro.web.servlet.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:)
at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:)
at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:)
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:)
at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:)
at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:)
at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:)
at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:)
at java.lang.Thread.run(Thread.java:)

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

     /**
* Method to validate a response to the challenge corresponding to the given ticket and remove the coresponding
* captcha from the store.
*
* @param ID the ticket provided by the buildCaptchaAndGetID method
* @return true if the response is correct, false otherwise.
* @throws CaptchaServiceException if the ticket is invalid
*/
public Boolean validateResponseForID(String ID, Object response)
throws CaptchaServiceException {
if (!store.hasCaptcha(ID)) {
throw new CaptchaServiceException("Invalid ID, could not validate unexisting or already validated captcha");
} else {
Boolean valid = store.getCaptcha(ID).validateResponse(response);
store.removeCaptcha(ID);
return valid;
}
}

其实,我也仔细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丢失的问题了。

  upstream cms {
server 10.130.14.51:;
server 10.130.14.53:;
ip_hash;
}

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

思考:

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

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

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

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

    一.安装 1. sudo apt-get install libsdl1.2-dev libsdl1.2debian sudo apt-get install libsdl1.2-dev(比较大,10 ...

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

    原文地址:http://www.cnblogs.com/tianguook/p/4142346.html 首先感谢aparche大牛的帖子,因为过两天可能要做个登录的页面,因此,需要用到验证码,从而看 ...

  3. 四、自动化平台搭建-Django-如何做验证码

    前提:安装包   pip install pillow==3.4.1 1.打开booktest/views.py,创建视图verify_code. from PIL import Image, Ima ...

  4. 使用Axure做验证码之校验验证码(二)

    本次作业,输入验证码,并校验验证码是否正确.上篇文章,介绍了如何获取验证码,本次作业在上次作业的基础上,做进一步的深究. 1.在上次作业中,增加新的元件: 文本框,命名:输入验证码: 增加热区,命名为 ...

  5. jcaptcha配置验证码

    准备开始 首先导入jar包:jcaptcha-my-1.0 /** * web 常量 * @author lx * */ public abstract class Constants { /** 用 ...

  6. node做验证码

    使用了ccap插件 1.安装: 通用方法:npm install ccap 2. cnst ccap= require('ccap')({ width: 128, height: 40, offset ...

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

    使用工具jTessBoxEditor-0.7(这个是在java平台下开发的,所以 它只支持java平台 ,在使用前应该先配置好java环境) tesseract 程序集(因为该程序集是在.net 2. ...

  8. Django做验证码登录

    验证码 关注公众号"轻松学编程"了解更多. 1.作用 在用户登录,注册以及一些敏感操作的时候,我们为了防止服务器被暴力请求,或爬虫爬取,我们可以使用验证码进行过滤,减轻服务器的压力 ...

  9. nodejs使用 svg-captcha 做验证码及验证

    一.需求 使用 nodejs 做后端开发,需要请求验证码,在 github 上看到了 svg-captcha 这个库,发现他是将 text 转 svg 进行返回的,安全性也有保证,不会被识别成文字. ...

随机推荐

  1. 转载:CancellationToken

    http://www.cnblogs.com/Abbey/archive/2011/09/12/2174208.html 最近在学习.NET中的线程同步.其中一个重要的技术叫线程的取消(中止),涉及的 ...

  2. 【转】oracle job相关内容

    每天凌晨2点执行是这样的 dbms_job.submit(v_job,'lv;',TRUNC(sysdate+1)+2/24,'TRUNC(sysdate+1)+2/24'); 还有定义JOB最好是这 ...

  3. const char* <----- > string

    (1) const char*      <-----     string const char* const_txt_path=txt_path.c_str(); (2)  string  ...

  4. Howto add permanent static routes in Ubuntu

    Static routing is the term used to refer to the manual method used to set up routing. An administrat ...

  5. C# 读取文本文档(转)

    1.添加命名空间 System.IO; System.Text; 2.文件的读取 (1).使用FileStream类进行文件的读取,并将它转换成char数组,然后输出. byte[] byData = ...

  6. 面向过程部分 Java 和 C++ 的区别

    前言 Java 和 C++ 在面向过程部分区别并不大,但还是有的,本文罗列了这些区别. 在 Java 中: 1. 数据类型的范围和机器无关 2. 加上前缀 0b 可以表示二进制数,如 0b1001 就 ...

  7. hdu 5093 Battle ships

    二分图匹配 #include<cstdio> #include<cstring> #include<iostream> #include<cmath> ...

  8. 12. Integer to Roman

    Given an integer, convert it to a roman numeral. Input is guaranteed to be within the range from 1 t ...

  9. leetcode 112 Path Sum ----- java

    Given a binary tree and a sum, determine if the tree has a root-to-leaf path such that adding up all ...

  10. 工作中遇到的问题--实现CustomerSetting的实时更新

    首先在项目运行时就初始化CustomerSettings的值,采用@Bean,默认是singtone模式,只会加载一次. @Configuration@Order(3)@EnableWebMvcSec ...