web-dev-qa-db-ja.com

getSession()が後続のリクエストで同じセッションを短い間隔で離さないのはなぜですか?

_$.getJSON_(HTTP GET)リクエストを2回(異なるデータで)送信しています(request1とrequest2があるとしましょう)。開発者ツールでFFとChrome=が同じ_cookie:JSESSIONID=FD0D502635EEB67E3D36203E26CBB59A_ヘッダーフィールドを持っていることがわかります。

サーバー側でセッションを取得しようとします。

_HttpSession session = request.getSession();
boolean isSessionNew = session.isNew();
String sessionId = session.getId();
String cookieFromRequestHeader = request.getHeader("cookie");
_

受け取った両方のリクエストに対してこれらの変数を出力すると、
リクエスト1:

isSessionNew:true
cookieFromRequestHeader:JSESSIONID = FD0D502635EEB67E3D36203E26CBB59A
session.getId():9212B14094AB92D0F7F10EE21F593E52

request2:

isSessionNew:true
cookieFromRequestHeader:JSESSIONID = FD0D502635EEB67E3D36203E26CBB59A
session.getId():E8734E413FA3D3FEBD4E38A7BF27BA58

ご覧のとおり、サーバーはrequest.getSession()にrequest2の新しいセッションを明確に作成しました。しかし、なぜこれを行うのですか?理論的には同期し、最初のリクエスト(このコードに最初に到達した)が作成したのと同じセッションを提供する必要があります。ここで、セッションの作成が同期していることを確認するには、次のようにします。

_@Autowired
private ServletContext servletContext;
...
synchronized (servletContext) {
    HttpSession session = request.getSession();
    boolean isSessionNew = session.isNew();
    String sessionId = session.getId();
    String cookieFromRequestHeader = request.getHeader("cookie");
}
_

そして私は同じ結果を得た。

同じリクエストを後でもう一度送信すると(request1 'とrequest2'と言います)、
request1 ':

isSessionNew:false
cookieFromRequestHeader:JSESSIONID = E8734E413FA3D3FEBD4E38A7BF27BA58 session.getId():E8734E413FA3D3FEBD4E38A7BF27BA58

request2 ':

isSessionNew:false
cookieFromRequestHeader:JSESSIONID = E8734E413FA3D3FEBD4E38A7BF27BA58
session.getId():E8734E413FA3D3FEBD4E38A7BF27BA58

今よく見ると、セッションIDは(request1 'とrequest2'で)同じであり、request2から作成された最後のIDです。非常に短い時間枠でサーバーに送信される複数の後続のリクエストから同じセッションを取得する方法はありますか?

私は特別な機能を使用していません-私はSpringのすぐに使えるセッション戦略を使用しています。また、最初のページ(request1およびrequest2)からのCookie JSESSIONIDは、最初にページにアクセスしたときに取得されたようです(このJSESSIONIDを作成したときに、サーバーにrequest0が送信されたとしましょう)。しかし、明示的にrequest.getSession()を呼び出さない限り、バックエンド/サーバーは常にすべての応答に対して新しいJSESSIONIDを作成し、それをクライアントに送り返します。したがって、応答が来た後に新しい要求がクライアントから送信されると、そのクライアントには新しいJSESSIONIDが設定されます。 Springのそのままのセッション処理が適切に機能していないようです。

敬具、
専制君主

追加研究

セッションの作成をHttpSessionListnerに登録できるかどうかを確認したいと思いました。このようにして、FD0D502635EEB67E3D36203E26CBB59AというIDのセッション(request1とrequest2で送信されているcookie)がいつ作成されたかを確認できます。また、リスナー(SessionProcessor)を使用して天気をセッションにIDで保存し、後でCookieからIDで取得することができます(別のセッションを作成する必要はありません)。
これがコードです:

_public interface ISessionProcessor extends ISessionRetriever, ISessionPopulator {
}

public interface ISessionRetriever {

    HttpSession getSession(String sessionId);
}

public interface ISessionPopulator {

    HttpSession setSession(String sessionId, HttpSession session);
}
_

これらを分離する理由は、リスナーがマップにセッションを追加することだけを許可し、コントローラーがrequest.getSession()を介してセッションを作成することのみを許可したかったためです。つまり、リスナーのsessionCreatedメソッドは常に呼び出されました(以下を参照してください)。

_public class SessionProcessor implements ISessionProcessor {

    private Map<String, HttpSession> sessions = new HashMap<String, HttpSession>();

    @Override
    public HttpSession getSession(String sessionId) {
            return sessions.get(sessionId);
    }

    @Override
    public HttpSession setSession(String sessionId, HttpSession session) {
            return sessions.put(sessionId, session);
    }

}

public class SessionRetrieverHttpSessionListener implements HttpSessionListener {

    private static final Logger LOGGER = LoggerFactory.getLogger(SessionRetrieverHttpSessionListener.class);

    @Autowired
    private ISessionPopulator sessionPopulator;

    @Override
    public void sessionCreated(HttpSessionEvent se) {
            HttpSession session = se.getSession();
            LOGGER.debug("Session with id {} created. MaxInactiveInterval: {} session:{}", new Object[]{session.getId(), session.getMaxInactiveInterval(), session});
            sessionPopulator.setSession(session.getId(), session);
    }

    @Override
    public void sessionDestroyed(HttpSessionEvent se) {
            HttpSession session = se.getSession();
            // session has been invalidated and all session data (except Id) is no longer available
            LOGGER.debug("Session with id {} destroyed. MaxInactiveInterval: {}, LastAccessedTime: {}, session:{}", 
                            new Object[]{session.getId(), session.getMaxInactiveInterval(), session.getLastAccessedTime(), session});
    }
}  
_

web.xml:org.springframework.web.context.ContextLoaderListener

_<servlet>
    <servlet-name>appServlet</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/spring/my-servlet-context.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>

<listener>
    <listener-class>mypackage.listener.SessionRetrieverHttpSessionListener</listener-class>
</listener>

<servlet-mapping>
    <servlet-name>appServlet</servlet-name>
    <url-pattern>/*</url-pattern>
</servlet-mapping>
_

my-servlet-context.xml内:

_<bean class="mypackage.listener.SessionProcessor"/>
<bean class="mypackage.SomeController"/>
_

私のコントローラーで:

_                    synchronized (servletContext) {
                            String cookieFromRequestHeader = request.getHeader("cookie");
                            LOG.debug("cookieFromRequestHeader:{}", new Object[] {cookieFromRequestHeader});
                            String jsessionIdFromCookieFromRequestHeader = cookieFromRequestHeader.substring(cookieFromRequestHeader.indexOf("=") + 1);
                            LOG.debug("jsessionIdFromCookieFromRequestHeader:{}", new Object[] {jsessionIdFromCookieFromRequestHeader});
                            session = sessionRetriever.getSession(jsessionIdFromCookieFromRequestHeader);
                            LOG.debug("session:{}", new Object[] {session});
                            if (session == null) {
                            LOG.debug("request.isRequestedSessionIdFromCookie():{}, request.isRequestedSessionIdFromURL():{}, WebUtils.getSessionId(request):{}.", new Object[] {request.isRequestedSessionIdFromCookie(), request.isRequestedSessionIdFromURL(), WebUtils.getSessionId(request)});
                            session = request.getSession();
                            boolean isSessionNew = session.isNew();
                            LOG.debug("Is session new? - {}. The session should not be new after the first fingerprint part is received - check if this occured in the logs - if that happend than there is an error!", isSessionNew);
                            LOG.debug("request.isRequestedSessionIdFromCookie():{}, request.isRequestedSessionIdFromURL():{}, WebUtils.getSessionId(request):{}.", new Object[] {request.isRequestedSessionIdFromCookie(), request.isRequestedSessionIdFromURL(), WebUtils.getSessionId(request)});
                            //read https://stackoverflow.com/a/2066883 and think about using ServletContextAware also.
                            LOG.debug("cookieFromRequestHeader:{} session.getId(): {}", new Object[]{cookieFromRequestHeader, session.getId()});
                            }
                    }
_

これは私に同じ結果を与えました。 request.getSession以外の方法でセッションを作成した場合(スプリング自体がセッションを作成したとき)は、リスナーによって登録されていないか、cookie/jsessionIDが他の場所から取得されたようです。詳しくは答えを見てください。

HttpSessionの問題を解決するのに役立つその他のソース
コントローラでのサーブレットコンテキストインジェクション
HttpSessionを使用する必要がある場合の同時実行の概要
HttpSessionオブジェクトを使用して同期を行う(これを回避する)
HttpSessionで作業するときに同期を行う「最良の」方法
春の参考資料:
セッション管理
セキュリティでのセッション管理
sessionIdがあるときにセッションを取得する方法についての議論(上記で行ったこと):
コードランチディスカッション
stackoverflow
リスナーの自動配線を完成させるのに役立った投稿

14
despot

最初の2つのリクエスト(request1とrequest2)からのCookie JSESSIONIDは、最初にページにアクセスしたときに取得されたようです(このJSESSIONIDの作成時にサーバーに送信されたrequest0があったとしましょう)。

これは真実ではありませんでした。同じサーバーの同じドメインに2つのアプリケーションをデプロイしています。したがって、 http://mydomain.com/app1/initpage を呼び出したときに、サーバーはID FD0D502635EEB67E3D36203E26CBB59Aを使用してapp1のセッションを作成し、このJSESSIONIDをCookieでクライアントに送信しました。クライアントがcookieをmydomain.comに保存し、2回目に http://mydomain.com/app2/executeService を実行したときに、クライアントのブラウザーがリクエストヘッダーのCookieからJSESSIONIDを送信しました。サーバーで受信しましたが、他のapp2のセッションではありませんでした。

これは、他の2つのリクエスト(request1 'とrequest2')を送信すると、適切なアプリケーションで作成されたセッションIDを持つという事実を説明しています。

詳細はこちら:
同じサーバーに複数のWebアプリをデプロイする
JSESSIONIDが作成される条件は何ですか?

私の質問に対する具体的な回答については、最初のリクエストを同期させる必要があるようですので、次のリクエストで常に同じセッションIDを持っていることを確認できます。ただし、最初のリクエストの後の次のリクエストは非同期になる可能性があります。

1
despot

Cookieを(JESSIONIDとともに)Clientに保存し、後続のリクエストをServerに送信するときに、保存されたCookieをリクエストヘッダーフィールドに入れて送信すると、サーバー側で同じセッションが取得されます。

CLIENT(IOS)レスポンスからのCookieを保存します:

    NSHTTPURLResponse* httpURLReqponse = (NSHTTPURLResponse*) response;
    NSDictionary* allHeaders = [httpURLReqponse allHeaderFields];
    NSLog(@"Response Headers : %@ ", allHeaders);
    NSString* cookie = [allHeaders objectForKey: @"Set-Cookie"];
    DATA.cookies = cookie;      // Store the cookie

CLIENT(IOS)Cookieを使用して後続のリクエストを送信します:

// Put the stored cookie in your request header
[(NSMutableURLRequest*)request addValue: DATA.cookies forHTTPHeaderField:@"cookie"];
[NSURLConnection sendAsynchronousRequest: request queue:[NSOperationQueue mainQueue] completionHandler:nil];

IOS clientの場合だけでなく、サーバー側でも同じセッションが得られます。

サーバー(JavaEE)GET HttpSession:

// Get the session, print its hashCode. You will find out that it's same as previous.
HttpSession session = ServletActionContext.getRequest().getSession();
0
isaacselement

...web.xmlファイルでCookieが無効になっているときに発生することに気づきました:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE glassfish-web-app PUBLIC "-//GlassFish.org//DTD GlassFish Application Server 3.1 Servlet 3.0//EN" "http://glassfish.org/dtds/glassfish-web-app_3_0-1.dtd">
<glassfish-web-app error-url="">

    <context-root>/some-app</context-root>

    <class-loader delegate="true"/>

    <jsp-config>
        <property name="keepgenerated" value="true">
            <description>Keep a copy of the generated servlet class' Java code.</description>
        </property>
    </jsp-config>

    <session-config>
        <session-properties>
            <property name="timeoutSeconds" value="600"/>
            <property name="enableCookies" value="false"/> 
        </session-properties>
    </session-config>

</glassfish-web-app>

同じ接続からのセッションIDを保持するには、<property name="enableCookies" value="false"/>にする必要があります。

0