为什么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。