由Nginx反向代理引出的JCaptcha驗證碼驗證失敗的問題

搜索關鍵字: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

一次好的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 😄 ~

相關文章
相關標籤/搜索