为什么不是的getSession()返回在随后的请求在短时间内相距相同的会话?(Why isn

2019-08-01 18:21发布

我送一个$.getJSON (HTTP GET)请求两次(在不同的数据),一个又一个的(可以说,我们有request1和请求2)。 我可以从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

正如你所看到的,显然服务器上创建的请求2的新会话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' 和请求2' )我得到的,
request1' :

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

请求2' :

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

如果您现在看到密切,会话ID是相同的(在request1'和请求2' ),并从请求2创建的最后一个。 有没有我的获取是从那来的服务器在很短的时间框架多个后续请求同一会话的方法吗?

我没有使用任何特殊的功能 - 我使用Spring的开箱即用的会话策略。 此外,它看起来像弗里斯特2个请求(request1和请求2)该cookie JSESSIONID来自我第一次访问该页面(可以说有向服务器发送一个request0创造了这个JSESSIONID时)。 但它也像,除非你明确地调用request.getSession(),后端/服务器将始终创建为每个响应的新的JSESSIONID,并将其发送回客户端。 因此,当一个新的请求从客户端的响应来后发送,其将会有一个新的JSESSIONID。 它看起来像春天开箱即用的会话处理不恰当地工作。

亲切的问候,
暴君

进一步的研究

我想看看我是否可以注册一个HttpSessionListner会话创建。 通过这种方式创建了一个带有ID FD0D502635EEB67E3D36203E26CBB59A(即在request1和请求2所发送的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>

在我-的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其他(当弹簧本身在箱子外面创建的会话),要么不被监听或饼干注册/ JSESSIONID来自其他地方。 寻找答案更多。

其他来源 ,帮助我去通过HttpSession的问题:
servlet上下文注射在控制器
并发的概述当你有HttpSession的工作
使用HttpSession对象做同步(避免这种情况)
与HttpSession中工作时,“最好”的方式做同步
一些春天参考的东西:
会话管理
在安全会话管理
就当你有一个的sessionId(我上面所说的那样)如何让会议的讨论:
coderanch讨论
堆栈溢出
后,帮助我完成我的监听器自动装配

Answer 1:

它看起来像从弗里斯特2个请求(request1和请求2)该cookie JSESSIONID来自我第一次访问该页面(可以说有向服务器发送一个request0创造了这个JSESSIONID时)。

这是不正确的。 我有同样的服务器在同一个域部署下2个应用程序。 所以,当我打电话http://mydomain.com/app1/initpage服务器创建与ID FD0D502635EEB67E3D36203E26CBB59A APP1会话,并在cookie发送到客户端这JSESSIONID。 客户端mydomain.com下保存该cookie时,我所执行的第二次http://mydomain.com/app2/executeService ,客户端浏览器从请求头中的Cookie发送的JSESSIONID。 我收到它在服务器上,但是这是不是在其他APP2会话。

这说明了,当我把其他两个请求(request1' 和请求2' ),他们有一个会话ID在相应的应用程序创建的。

看看更多在这里:
在部署同一台服务器的多个Web应用程序
在什么情况下是一个JSESSIONID产生的?

至于具体的回答我的问题, 看来你需要做的第一个要求同步,因此你总是确保您有以下请求相同的会话ID。 一日一后,下面的请求,可以是异步的,虽然。



Answer 2:

只要您的Cookie(与JESSIONID)存储在客户端,当你发送后续请求到服务器,把存储的cookie在请求头字段和发送,那么你会得到在服务器端同一个会话。

客户端(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

客户端(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();


Answer 3:

我注意到,当它发生的cookie是禁用...web.xml文件:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE glassfish-web-app PUBLIC "-//GlassFish.org//DTD GlassFish Application Server 3.1 Servlet 3.0//EN" "http://glassfish.org/dtds/glassfish-web-app_3_0-1.dtd">
<glassfish-web-app error-url="">

    <context-root>/some-app</context-root>

    <class-loader delegate="true"/>

    <jsp-config>
        <property name="keepgenerated" value="true">
            <description>Keep a copy of the generated servlet class' java code.</description>
        </property>
    </jsp-config>

    <session-config>
        <session-properties>
            <property name="timeoutSeconds" value="600"/>
            <property name="enableCookies" value="false"/> 
        </session-properties>
    </session-config>

</glassfish-web-app>

它应该是<property name="enableCookies" value="false"/>以从相同的连接保留的会话ID。



文章来源: Why isn't getSession() returning the same session in subsequent requests distanced in short time periods?