当前位置: 首页 > 面试题库 >

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

向泽语
2023-03-14
问题内容

我正在发送$.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

如您所见,服务器清楚地在上为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以外的其他方式创建会话(当弹簧本身开箱即用创建会话时)不是由侦听器注册的,还是cookie
/ jsessionID来自其他地方。寻找更多答案。

其他 来源 ,帮助我去通过HttpSession的问题:
在控制器servlet上下文注入
并发的概述当你将工作与HttpSession中
使用HttpSession对象做同步(避免这个)
与HttpSession中工作时,“最好”的方式做同步
一些春天参考资料:安全性 讨论中的
会话管理
会话管理,
有关在拥有sessionId时如何获取会话的讨论(我在上面做了什么):
coderanch讨论
stackoverflow
有助于我完成侦听器自动装配的帖子


问题答案:

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

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

这解释了以下事实:当我发送其他两个请求(request1’和request2’)时,它们在适当的应用程序上创建了一个sessionID。

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



 类似资料:
  • 问题内容: 我有一个使用创建的。然后,我计算出与该时间之间的纳秒数。 然后,我花了十亿分之一秒,然后将它们重新变成了use 。 但是,如果我使用来比较重构时间和原始时间,则返回false。如果我减去这2次,则得到的持续时间为0。如果我使用进行比较,则返回true。 如果我使用与第一次相同的值来创建另一个时间,则使用比较此新时间和原始时间将得出真。 这是演示此代码的代码(Golang Playgro

  • 问题内容: 我正在尝试通过COM端口发送AT命令,但只重新发送了相同的命令。 日志: 16:19:21.910 [main]调试SerialConnections.M234Serial-创建实例。 16:19:21.974 [main]调试SerialConnections.M234Serial-发送请求:AT ^ SCFG? 16:19:23.976 [EventThread COM55]调试S

  • 问题内容: 今天,我了解并决定将其使用和测试。我知道整数是不可变的,因此id应该是(?)相同。但是,当我在提示中进行测试时,我注意到了一些细微的差异,并想找出其背后的原因。 凉!到目前为止,所有签出。但是之后… 好的,所以测试一下,我注意到此行为一直持续到256年。id最多可以是8位数字,然后257将返回更大的id,可以是15位数字。因此类型必须是8个字节。 所以我发现它与8个字节长有关,但是任何

  • 问题内容: 以下是ajax请求。 这就是delete.php 运行代码后,它将成功删除文件,但不会显示任何消息。 我也尝试将ajax请求更改为: 仍然不显示该消息。所以我想在delete.php文件中出了点问题。请帮忙。 问题答案: 进行jquery + ajax + php的最佳方法如下: jQuery的: PHP:

  • 当我执行普通Select时,返回正确的结果,但当我执行Select for DB uptime时,它始终返回相同的第一个结果。我确实检查了Postgres日志,我看到select被执行了。

  • 问题内容: 我有下面的代码。我只想检查代码块的运行时间。错误地,我再次复制并粘贴了相同的代码,并得到了有趣的结果。尽管代码块相同,但运行时间不同。而且 比其他人花费更多的时间。如果我切换代码块,则代码块4将比其他代码花费更多时间。 我在代码块中使用了两种不同类型的数组来检查它是否依赖于此。结果是一样的。如果代码块具有相同类型的数组,则最上面的代码块将花费更多时间。参见下面的代码和给出的输出。 运行