14

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

サーバー側では、セッションを取得しようとしています:

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

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

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

リクエスト2:

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

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

@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 から最後に作成されたものです。非常に短い時間枠でサーバーに来る複数の後続のリクエストから同じセッションを取得する方法はありますか?

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

敬具、
デスポット

追加の研究:

セッションの作成を HttpSessionListner に登録できるかどうかを確認したかったのです。このようにして、ID FD0D502635EEB67E3D36203E26CBB59A (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 以外の方法によるセッションの作成 (Spring 自体がセッションを作成したとき) は、リスナーによって登録されていないか、cookie/jsessionID が別の場所から取得されたようです。詳細については、答えを見てください。

HttpSessionの問題を解決するのに役立ったその他の情報源:
コントローラーでのサーブレット コンテキスト インジェクション同期を行うために HttpSession オブジェクトを使用して HttpSessionを操作
する必要がある場合の同時実行の概要
(これを回避する)参考資料: sessionId がある場合にセッションを取得する方法に関するセキュリティディスカッションでのセッション管理セッション管理 (上記で行ったこと): coderanchディスカッションstackoverflow







4

3 に答える 3

1

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

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

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

詳細はこちら:
Deploying multiple web apps in same server
JSESSIONID はどのような条件下で作成されますか?

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

于 2012-10-24T10:51:03.543 に答える
0

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 クライアントだけではありません。次に、サーバー側で同じセッションを取得します。

サーバー (JavaEE) GET HttpSession:

// Get the session, print its hashCode. You will find out that it's same as previous.
HttpSession session = ServletActionContext.getRequest().getSession();
于 2013-10-19T02:27:04.403 に答える