(HTTP GET)リクエストを(異なるデータで)2回送信してい$.getJSON
ます(request1とrequest2があるとしましょう)。cookie:JSESSIONID=FD0D502635EEB67E3D36203E26CBB59A
FF と 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