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

des*_*pot 14 java jquery spring http-get httpsession

我发送一个$.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");
Run Code Online (Sandbox Code Playgroud)

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

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

请求2:

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

如您所见,服务器在a上为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");
}
Run Code Online (Sandbox Code Playgroud)

我得到了同样的结果.

如果我再次发送相同的请求(比如request1'和request2'),我得到,
request1':

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

请求2' :

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检索它们(因此我不需要创建另一个会话).
所以这是代码:

public interface ISessionProcessor extends ISessionRetriever, ISessionPopulator {
}

public interface ISessionRetriever {

    HttpSession getSession(String sessionId);
}

public interface ISessionPopulator {

    HttpSession setSession(String sessionId, HttpSession session);
}
Run Code Online (Sandbox Code Playgroud)

分离这些的原因是因为我只想允许侦听器向地图添加会话,而控制器只能通过request.getSession()创建会话 - 所以始终调用listner的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});
    }
}  
Run Code Online (Sandbox Code Playgroud)

在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>
Run Code Online (Sandbox Code Playgroud)

在my-servlet-context.xml中:

<bean class="mypackage.listener.SessionProcessor"/>
<bean class="mypackage.SomeController"/>
Run Code Online (Sandbox Code Playgroud)

在我的控制器中:

                    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()});
                            }
                    }
Run Code Online (Sandbox Code Playgroud)

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

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

des*_*pot 0

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

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

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

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

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


归档时间:

查看次数:

33627 次

最近记录:

10 年,3 月 前