JavaでServletプログラムを作成していてセッションを使っていたのですが、なぜか request.getSession(false); がnullになってしまう事象にあいました。
初回アクセス時
HttpSession session = request.getSession( true );
二回目以降
HttpSession session = request.getSession( false );
session.getAttribute("xxx"); でセッション属性を取得したいのだけれど、sessionがnullのため取得できない。。アレコレ悩みました。
今回のウェブサーバーは Nginx + Tomcat の構成(同一サーバーに相乗り)なのですが、Nginxの proxy_pass ディレクティブでTomcatへの転送先URLを指定しています。
proxy_pass http://localhost:8080/test/;
末尾の /test/ はウェブアプリケーションのコンテキストパスです。実際にServletにアクセスするURLは、以下のようにしています。
http://tomcat/sample/fwservlet
URL中の tomcat がドメインで fwservlet はServlet名です。Nginxを介さずに直接Tomcatにアクセスする場合は、このようになります(localhost はIPアドレスに読み替えてください)。
http://localhost:8080/test/sample/fwservlet
ここでNginxに向けてリクエストをするとダメなのだけれど、直接Tomcatに向けてリクエストをするとうまくいくことに気づきました。原因はNginxにあるのか?と思いつつも、Firefoxのデバッグモードでクッキーを見てみました。リクエストはNginxに向けて投げています。
これを見るとクッキーには JSESSIONID が設定されていることが確認できました。なので初回アクセス時に新規セッションは問題なく作成されているようです。そうすると、なぜ二回目以降のアクセスでこのセッションが取得できないのか?になるのですが、クッキーのPath属性を見ると /test となっています。
Path属性は「送信するURLにここで指定するパスが含まれている場合のみクッキーが送信される」ということになるのですが、Nginx向けのリクエストURLには /test はないです。直接Tomcatに向ける場合は含まれています。つまり、Nginxに向けて投げた場合、クッキーが送信されていないから request.getSession( false ); で JSESSIONID が取得できず、nullになってしまっていることがわかりました。
この解決策ですが、/test/ のコンテキストパスを変えるのはいまさらだとインパクトが大きい。なので web.xml のサーブレットマッピングをいじることで対応しました。リクエストURLに強引に /test のパスを含めるようなやり方です。
こんな感じです。
<servlet> <servlet-name>fwservlet</servlet-name> <servlet-class>sample.fwservlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>fwservlet</servlet-name> <url-pattern>/test/fwservlet</url-pattern> </servlet-mapping>
JSPも同様に対応します。
<servlet> <servlet-name>nolist</servlet-name> <jsp-file>/sample/nolist.jsp</jsp-file> </servlet> <servlet-mapping> <servlet-name>nolist</servlet-name> <url-pattern>/test/nolist.jsp</url-pattern> </servlet-mapping>
この設定をするとNginxへのリクエストURLは以下のように変わります。
http://tomcat/test/fwservlet
これでServletを動かすと request.getSession( false ); でセッションを取得することができるようになりました。