为什么getSession()在后续请求中在短时间内返回相同的会话? [英] Why isn't getSession() returning the same session in subsequent requests distanced in short time periods?

查看:155
本文介绍了为什么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: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创建的最后一个。有没有办法让我在很短的时间内从多个后续请求获得相同的会话?



我没有使用任何特殊的功能 - 我正在使用Spring的开箱即用会话策略。此外,它看起来像frist 2请求(request1和request2)的cookie JSESSIONID来自我第一次访问页面时(假设在创建此JSESSIONID时有一个request0发送到服务器)。但它看起来除非你明确调用request.getSession(),否则后端/服务器将始终为每个响应创建一个新的JSESSIONID并将其发送回客户端。因此,当响应到来之后从客户端发送新请求时,它将具有新的JSESSIONID。看起来Spring开箱即用的会话处理工作不正常。



亲切的问候,

暴君



其他研究



我想知道是否可以使用HttpSessionListner注册会话创建。这样我就可以看到与id FD0D502635EEB67E3D36203E26CBB59A(在request1和request2中发送的cookie)的会话何时被创建。而且,使用侦听器(SessionProcessor)的天气我可以通过id将会话存储在地图中,然后通过cookie中的id检索它们(所以我不需要创建另一个会话)。

所以这里是代码:

 公共接口ISessionProcessor扩展ISessionRetriever,ISessionPopulator {
}

公共接口ISessionRetriever {

HttpSession getSession(String sessionId);
}

公共接口ISessionPopulator {

HttpSession setSession(String sessionId,HttpSession session);
}

分隔这些的原因是因为我只想让听众添加会话到地图,控制器只能通过request.getSession()创建会话 - 所以总是调用listner的sessionCreated方法(如下所示)。

 公共类SessionProcessor实现ISessionProcessor {

私有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);
}

}

公共类SessionRetrieverHttpSessionListener实现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(创建了身份{}的会话.MaxInactiveInterval:{} session:{},new Object [] {session.getId(),session.getMaxInactiveInterval(),session});
sessionPopulator.setSession(session.getId(),session);
}

@Override
public void sessionDestroyed(HttpSessionEvent se){
HttpSession session = se.getSession();
//会话已失效,所有会话数据(Id除外)不再可用
LOGGER.debug(id {}会话已销毁.MaxInactiveInterval:{},LastAccessedTime:{},session: {},
new Object [] {session.getId(),session.getMaxInactiveInterval(),session.getLastAccessedTime(),session});在$ web

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(会话是新的吗? - {}。收到第一个指纹部分后,会话不应该是新的 - 检查日志中是否发生这种情况 - 如果发生这种情况,则会出现错误!, isSessionNew);
LOG.debug(request.isRequestedSessionIdFromCookie():{},request.isRequestedSessionIdFromURL():{},WebUtils.getSessionId(request):{}。,new Object [] {request.isRequestedSessionIdFromCookie(), request.isRequestedSessionIdFromURL(),WebUtils.getSessionId(request)});
//阅读https://stackoverflow.com/a/2066883并考虑使用ServletContextAware。
LOG.debug(cookieFromRequestHeader:{} session.getId():{},new Object [] {cookieFromRequestHeader,session.getId()});
}
}

这给了我相同的结果。似乎通过除request.getSession以外的方式创建会话(当弹簧本身开箱即创建会话时),或者没有被监听器注册,或者cookie / jsessionID来自其他地方。再看看答案。



帮助我解决HttpSession问题的其他来源

控制器中的服务器上下文注入

必须使用HttpSession时的并发概述
b $ b 使用HttpSession对象进行同步(避免这种情况) < br>
工作时进行同步的最佳方式使用HttpSession

一些春季参考资料:

会话管理

安全会话管理

讨论如何在有sessionId时获取会话(我上面做了什么):

coderanch discussion

stackoverflow

帮助我完成听众自动装配的帖子

解决方案


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


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



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



在这里看看更多:

在同一服务器中部署多个Web应用程序

在什么条件下创建JSESSIONID?



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


I am sending a $.getJSON (HTTP GET) request twice (with different data), one after another (lets say we have request1 and request2). I can see in the developer tools from FF and Chrome that I have the same cookie:JSESSIONID=FD0D502635EEB67E3D36203E26CBB59A header field.

On the server side I try to get the session:

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

If I print these variables for both requests I get,
request1:

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

request2:

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

As you can see, the server clearly created a new session for request2 on a request.getSession(). But why does it do this? It should theoretically be synchronized and give you the same session that the first request (that reached this code first) created. Now, to be sure that the session creation is synchronized I did the following:

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

and I got the same results.

If I send the same requests later again (lets say request1' and request2') I get,
request1':

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

request2':

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

If you see closely now, the session id is the same (in request1' and request2') and is the last one created from the request2. Is there a way of me getting the same session from multiple subsequent requests that come to the server in very short time frames?

I am not using any special features - I am using Spring's out of the box session strategy. Also, it looks like the cookie JSESSIONID from the frist 2 requests (request1 and request2) come from the first time I visit the page (lets say there was a request0 sent to the server when it created this JSESSIONID). But it also looks like unless you explicitly call request.getSession(), the backend/server will always create a new JSESSIONID for every response and send it back to the client. So when a new request is sent from the client after a response comes, its going to have a new JSESSIONID. It looks like Spring out of the box session handling is not working appropriately.

Kind Regards,
despot

ADDITIONAL RESEARCH:

I wanted to see if I can register the session creation with a HttpSessionListner. This way I can see when the session with id FD0D502635EEB67E3D36203E26CBB59A (the cookie that is being sent in request1 and request2) is created. And also, weather using the listener (the SessionProcessor) I can store the sessions in a map by id and later on retrieve them by the id from the cookie (so I don't need to create another session).
So here is the code:

public interface ISessionProcessor extends ISessionRetriever, ISessionPopulator {
}

public interface ISessionRetriever {

    HttpSession getSession(String sessionId);
}

public interface ISessionPopulator {

    HttpSession setSession(String sessionId, HttpSession session);
}

The reason for separating these was because I only wanted to allow the listener to add sessions to the map, and the controllers only to be able to create a session through request.getSession() - so the listner's sessionCreated method was invoked always (as you'll see below).

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});
    }
}  

in 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>

in my-servlet-context.xml:

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

in my controller:

                    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()});
                            }
                    }

This gave me the same results. It appeared that the session creation by means other than request.getSession (when spring itself out of box created the session), was either not registered by the listener or the cookie/jsessionID came from somewhere else. Look the answer for more.

Other sources that helped me go through the HttpSession issues:
servlet context injection in controller
overview of concurrency when you have to work with HttpSession
using HttpSession object to do synchronization (avoid this)
the "best" way to do synchronization when working with HttpSession
some spring reference stuff:
session management
session management in security
discussions on how to get session when you have a sessionId (what I did above):
coderanch discussion
stackoverflow
the post that helped me finalize my listener autowiring

解决方案

it looks like the cookie JSESSIONID from the frist 2 requests (request1 and request2) come from the first time I visit the page (lets say there was a request0 sent to the server when it created this JSESSIONID).

This was not true. I have 2 applications deployed under the same domain on the same server. So when I was calling http://mydomain.com/app1/initpage the server created a session for app1 with id FD0D502635EEB67E3D36203E26CBB59A and sent this JSESSIONID in a cookie to the client. The client saved the cookie under the mydomain.com and the second time when I executed http://mydomain.com/app2/executeService, the client browser sent the JSESSIONID from the cookie in the request header. I received it on the server but this was not a session in the other app2.

This explains the fact that when I send the other two requests (request1' and request2') they have a sessionID created on the appropriate application.

Have a look more here:
Deploying multiple web apps in same server
Under what conditions is a JSESSIONID created?

As for the concrete answer to my question, it appears that you need to make the 1st request synchronized so you are always sure that you have the same session id in the following requests. The following requests after the 1st one, can be asynchronous though.

这篇关于为什么getSession()在后续请求中在短时间内返回相同的会话?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆