搜索关键字:html
1)Windows本地开发正常,部署到Linux远程服务器上JCaptcha验证失败java
2)Linux远程服务器上JCpatcha验证失败nginx
3)Nginx反向代理后JCaptcha验证失败web
我为何要写这篇文章?apache
很简单,由于从我遇到这个问题到解决这个问题,途中花了很多时间,查了很多资料,改了很多代码,验证了很多猜测。然而,最后解决问题,只须要在 nginx.conf 中加一行配置便可。vim
为何这么一个 “小问题” 要花我这么多时间呢?浏览器
由于 “JCpatcha验证码验证失败” 只是表象,问题的本质缘由是 “Nginx反向代理致使Session丢失”。而大多数对知识点没有深刻理解的、缺少经验的同窗(好比我),一开始都只会根据表象去查询解决方案,收效甚微。使用 “Nginx反向代理致使Session失效” 等关键字去查,解决方案一查一大堆,而使用 “Linux服务器下JCaptcha验证码失败” 相似的关键字去搜索,每每很难找到解决该问题的方法,由于该问法的范围较广,没有针对性(抓住关键点)。服务器
因此,我写了这篇文章,而且特地在文章顶部写了搜索关键字,但愿能够帮助遇到一样问题的同窗提升搜索效率。除了写解决问题的方法外,我还贴出了从遇到这个问题到解决问题这一路的Debug过程,或许我思考问题的方式、验证猜测的方法等能够给你们一些帮助😁。cookie
回到目录session
最近写工程实践项目,使用了JCaptcha来作验证码,在Windows下本地测试是正常的,但部署到远程Linux服务器后,发现用户登陆时(不单是用户登陆,其余用到验证码的功能都同样),始终返回“验证码错误”。
以前在Windows下开发测试时,验证码部分遇到过的报错是:“com.octo.captcha.service.CaptchaServiceException: Invalid ID, could not validate unexisting or already validated captcha”。我觉得也是这个问题呢,结果一看日志,啥报错都没有。
1 问题
问题的根本缘由,其实从文章标题就能够看出,就是 使用Nginx反向代理后Session丢失了,而JCpatcha是基于Session来实现的。看下面的图吧(不专业,见笑了):
2 解决
既然是 Nginx反向代理session丢失引发的问题,那咱们让 Nginx反向代理时保持session不就行了。关于 解决Nginx反向代理session丢失问题 的方法网上一查一大堆,而我是用下面的方法(简单加一行配置)解决的。
> sudo vim /etc/nginx/nginx.conf,添加:proxy_cookie_path /idevtools /; # 保持session,根据你的项目更改path,proxy_cookie_path [path1] [path2]; 后面接的是2个path,注意它们的顺序;改完保存、退出,> sudo service nginx restart,OK~打完收工。
user www-data; worker_processes auto; pid /run/nginx.pid; events { worker_connections 768; # multi_accept on; } http { ## 省略其余配置## # SSL Settings # add https ssl [southday 2018.11.1 v1] server { listen 443; server_name www.idevtools.cn; ssl on; ssl_certificate /etc/nginx/cert/cert-1540964531179_www.idevtools.cn.crt; ssl_certificate_key /etc/nginx/cert/cert-1540964531179_www.idevtools.cn.key; ssl_session_timeout 5m; ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4; ssl_protocols TLSv1 TLSv1.1 TLSv1.2; ssl_prefer_server_ciphers on; # 关键步骤:将https://idevtools.cn/ 映射到 http://localhost:8090/idevtools/ location / { proxy_pass http://localhost:8090/idevtools/;
proxy_cookie_path /idevtools /; # 保持session } } ## 省略其余配置 }
3 参考资料
一次好的debug就像是一场探险寻宝之旅,翻山越岭,披荆斩刺,最终找到宝藏。在这个过程当中,不只能够增加你的经验,还能够加深你对事物的认知,最后还解决了问题,拿到了专属于你的宝藏。
因此,不要轻易放过一个bug,也不要浮躁,花点时间去研究与实践,总会有收获的😄。下面的内容就是我在遇到这个问题(Windows本地开发测试正常,部署到Linux服务器上JCaptcha验证失败)时的探险寻宝之旅~
1 为何Windows本地开发测试正常,部署到Linux服务器上JCaptcha验证失败?
第一反映,查日志,看看是否是报了什么异常,发现日志中没有关于JCaptcha验证的异常信息。
2 为何没有异常信息,却返回验证失败呢?
看代码,包括JCaptcha验证的部分源码。JCaptcha中验证方法的入口是:com.octo.captcha.module.servlet.image.SimpleImageCaptchaServlet 类的 validateResponse() 方法,源码以下:
public static boolean validateResponse(HttpServletRequest request, String userCaptchaResponse) { if (request.getSession(false) == null) { return false; } else { boolean validated = false; try { validated = service.validateResponseForID(request.getSession().getId(), userCaptchaResponse); } catch (CaptchaServiceException var4) { var4.printStackTrace(); } return validated; } }
注意,我直接找该方法来排查问题,是由于我已经确保了个人调用流程中,其余步骤都能正确获取到值、正确返回结果。若是你还不能确保,那仍是老老实实run代码,测试一遍。好多时候找不到bug是由于细节问题没处理好,还有自觉得是的 “啊,这个地方没有问题的,我保证!”😂
其实到这里,问题也就很明显了,没有报异常,那颇有可能就是代码走了这条路径:
if (request.getSession(false) == null) { return false; }
我为何这么敢确定呢?由于我是有办法让 “com.octo.captcha.service.CaptchaServiceException: Invalid ID, could not validate unexisting or already validated captcha” 这个异常重现的,而我部署到服务器上时,实施了重现这个异常的操做,发现却没有任何报错信息。
(上述的都是后话,由于我从没想过会有session丢失这种问题,思惟被定式到了“异常信息没有展现”上,因此走了一些弯路,但也学了一些东西)
3 弯路
个人思惟被定式到了“异常信息”上,因此在想,是否是代码内部其实抛了异常,只是部署到服务器上,var4.printStackTrace() 的内容不会打印到日志中?
顺着这个思路,我找了:如何将 printStackTrace() 中的内容打印到日志中。查到的好多结果都是:用Logger记录就OK,差很少就是下面的语句:
import org.apache.commons.lang3.exception.ExceptionUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; static Logger logger = LogManager.getLog(SimpleImageCaptchaServlet.class); ... try { validated = service.validateResponseForID(request.getSession().getId(), userCaptchaResponse); } catch (CaptchaServiceException var4) { logger.warn(var4); // 或者 logger.warn(ExceptionUtils.getStackTrace(var4))); } ...
可是,SimpleImageCaptchaServlet 是JCaptcha的源码啊,难道我要下载一份源码,本身DIY,从新编译打包,而后在项目中添加DIY后的jar依赖吗?其实事情没那么复杂,我只须要从新写一个类:MySimpleImageCaptchaServlet.java,其内部使用和SimpleImageCaptchaServlet差很少的实现,只是本身加了一些打印语句(方便调试),而后 web.xml 中配置 /jcaptcha 的Servlet为MySimpleImageCaptchaServlet便可。
经过上述的方法,构造了MySimpleImageCaptchaServlet.java,以下(只贴validateResponse()部分):
public static boolean validateResponse(HttpServletRequest request, String userCaptchaResponse) {if (request.getSession(false) == null) { return false; } else { boolean validated = false; try { logger.info("[before] validateResponse(), validated = " + validated); validated = service.validateResponseForID(request.getSession().getId(), userCaptchaResponse); logger.info("[after] validateResponse(), validated = " + validated); } catch (CaptchaServiceException var4) { logger.warn("验证码验证异常:" + ExceptionUtils.getStackTrace(var4)); } return validated; } }
从新部署到服务器上,再次测试,发现日志中依旧没有异常信息,此外,连 “[before] validateResponse(), validated = ”这些信息都没打印,这不就是没执行 try{}catch{}中的语句嘛!!!
弯路到此结束,我已经知道是走上面的 if(request.getSession(false) == null) {} 路径了。
4 那么 request.getSession(false) 为何为空呢?
想起来了,在Windows上开发测试,与部署到Linux服务器上的区别,除了操做系统外,还有一个比较重要的地方被我遗漏了,Nginx反向代理!
(过后,我把Nginx关了,直接用 http://idevtools.cn:8090/idevtools/ 去测试,是能够成功登陆的)
因而在网上查:Nginx反向代理、request.getSession(),一大堆资料。(这里吐槽一下CSDN和那些Copyer,我查个资料,前7条都是同样的标题名称???点进去一看,内容几乎差很少,能来个原创吗?CSDN不能用印象笔记浏览器插件剪贴文章是什么鬼?f++u)
剩下的内容就能够参考第三部分了,👉 转送门
虽然我这里的debug过程只列出了4步,但真实状况要比这还麻烦一些,只是出于时间关系,以及问题描述等缘由,我作了一些简化。总得来讲,此次debug之旅仍是有些收获的,也不枉费我花了这么多时间。最后,one more time,一次好的debug就像是一场探险寻宝之旅,翻山越岭,披荆斩刺,最终找到宝藏。在这个过程当中,不只能够增加你的经验,还能够加深你对事物的认知,最后还解决了问题,拿到了专属于你的宝藏。因此,不要轻易放过一个bug,也不要浮躁,花点时间去研究与实践,总会有收获的😄。
转载请说明出处!have a good time 😄 ~