为什么getSession()在后续请求中在短时间内返回相同的会话?

我一个接一个地发送$.getJSON (HTTP GET)请求(使用不同的数据)(假设我们有request1和request2)。 我可以在FF和Chrome的开发者工具中看到我有相同的cookie:JSESSIONID=FD0D502635EEB67E3D36203E26CBB59A标题字段。

在服务器端,我尝试获取会话:

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

如果我为这两个请求打印这些变量,
request1:

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

请求2:

isSessionNew:真
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:假
cookieFromRequestHeader:JSESSIONID = E8734E413FA3D3FEBD4E38A7BF27BA58 session.getId():E8734E413FA3D3FEBD4E38A7BF27BA58

请求2′ :

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

如果你现在仔细看,会话ID是相同的(在request1’和request2’中),并且是从request2创建的最后一个。 有没有办法让我在很短的时间内从多个后续请求中获得相同的会话?

我没有使用任何特殊function – 我正在使用Spring的开箱即用会话策略。 此外,它看起来像frist 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()创建会话 – 所以始终调用listner的sessionCreated方法(就像你一样)我会在下面看到)。

 public class SessionProcessor implements ISessionProcessor { private Map sessions = new HashMap(); @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

  appServlet org.springframework.web.servlet.DispatcherServlet  contextConfigLocation /WEB-INF/spring/my-servlet-context.xml  1   mypackage.listener.SessionRetrieverHttpSessionListener   appServlet /*  

在my-servlet-context.xml中:

   

在我的控制器中:

  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问题的其他来源
控制器中的servlet上下文注入
必须使用HttpSession时的并发概述
使用HttpSession对象进行同步(避免这种情况)
使用HttpSession时进行同步的“最佳”方式
一些春季参考资料:
会话管理
安全会话管理
讨论如何在有sessionId时获取会话(我上面做了什么):
代码讨论
堆栈溢出
帮助我完成听众自动assembly的post

它看起来像frist 2请求(request1和request2)中的cookie JSESSIONID来自我第一次访问页面时(假设在创建此JSESSIONID时有一个request0发送到服务器)。

事实并非如此。 我在同一台服务器上的同一域下部署了2个应用程序。 因此,当我调用http://mydomain.com/app1/initpage时 ,服务器为app1创建了一个ID为FD0D502635EEB67E3D36203E26CBB59A的会话,并将这个JSESSIONID以cookie的forms发送给客户端。 客户端将cookie保存在mydomain.com下,第二次执行http://mydomain.com/app2/executeService时 ,客户端浏览器从请求头中的cookie发送了JSESSIONID。 我在服务器上收到了它,但这不是其他app2中的会话。

这解释了当我发送另外两个请求(request1’和request2’)时,他们在相应的应用程序上创建了一个sessionID。

看看更多:
在同一服务器中部署多个Web应用程序
在什么条件下创建了JSESSIONID?

至于我的问题的具体答案, 似乎您需要使第一个请求同步,因此您始终确保在以下请求中具有相同的会话ID。 第一个之后的以下请求可以是异步的。

只需将您的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(); 

我注意到在...web.xml文件中禁用cookie时会发生这种情况:

    /some-app    Keep a copy of the generated servlet class' java code.          

它应该是以保留来自同一连接的会话ID。