Почему 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

запрос2:

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

запрос2 ':

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

Теперь, если вы внимательно посмотрите, идентификатор сеанса тот же (в request1 'и request2') и является последним, созданным из запроса2. Есть ли способ получить один и тот же сеанс из нескольких последующих запросов, поступающих на сервер за очень короткие промежутки времени?

Я не использую никаких специальных функций - я использую готовую стратегию сеансов Spring. Кроме того, похоже, что файл cookie JSESSIONID из первых 2 запросов (request1 и request2) поступает с первого раза, когда я посещаю страницу (допустим, на сервер был отправлен запрос 0, когда он создал этот JSESSIONID). Но также похоже, что если вы явно не вызываете request.getSession (), бэкэнд / сервер всегда будет создавать новый JSESSIONID для каждого ответа и отправлять его обратно клиенту. Поэтому, когда новый запрос отправляется от клиента после получения ответа, у него будет новый JSESSIONID. Похоже, что Spring обработка сеанса из коробки не работает должным образом.

С уважением, деспот

ДОПОЛНИТЕЛЬНОЕ ИССЛЕДОВАНИЕ:

Я хотел посмотреть, могу ли я зарегистрировать создание сеанса с помощью HttpSessionListner. Таким образом, я могу видеть, когда создается сеанс с идентификатором FD0D502635EEB67E3D36203E26CBB59A (файл cookie, отправляемый в request1 и request2). А также погодные условия с помощью слушателя (SessionProcessor). Я могу сохранять сеансы на карте по идентификатору, а затем извлекать их по идентификатору из файла cookie (так что мне не нужно создавать еще один сеанс).
Итак, вот это код:

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 (когда сам Spring из коробки создал сеанс), либо не было зарегистрировано слушателем, либо cookie / jsessionID пришел откуда-то еще. Поищите ответ, чтобы узнать больше.

Другие источники, которые помогли мне решить проблемы с HttpSession:
внедрение контекста сервлета в контроллер
обзор параллелизма при работе с HttpSession
с помощью объекта HttpSession для синхронизации (избегайте этого)
" лучший "способ синхронизации при работе с HttpSession
некоторые справочные материалы по Spring:
управление сеансом
управление сеансом в безопасности
обсуждения о том, как получить сеанс когда у вас есть sessionId (что я сделал выше):
обсуждение кода ветвления
stackoverflow
сообщение, которое помогло мне завершить автоматическое подключение слушателя


person despot    schedule 22.10.2012    source источник


Ответы (3)


похоже, что файл cookie JSESSIONID из первых 2 запросов (request1 и request2) поступает с первого раза, когда я посещаю страницу (допустим, на сервер был отправлен запрос 0, когда он создал этот JSESSIONID).

Это было неправдой. У меня есть 2 приложения, развернутых в одном домене на одном сервере. Поэтому, когда я звонил http://mydomain.com/app1/initpage, сервер создал сеанс для app1 с идентификатором FD0D502635EEB67E3D36203E26CBB59A и отправил этот JSESSIONID в файле cookie клиенту. Клиент сохранил файл cookie под mydomain.com и второй раз, когда я выполнил http://mydomain.com/app2/executeService, браузер клиента отправил JSESSIONID из файла cookie в заголовке запроса. Я получил его на сервере, но это не был сеанс в другом приложении2.

Это объясняет тот факт, что когда я отправляю два других запроса (request1 'и request2'), у них создается идентификатор сеанса в соответствующем приложении.

Подробнее см. Здесь:
Развертывание нескольких веб-приложений в тот же сервер
При каких условиях создается JSESSIONID? < / а>

Что касается конкретного ответа на мой вопрос, похоже, что вам нужно синхронизировать 1-й запрос, чтобы вы всегда были уверены, что у вас одинаковый идентификатор сеанса в следующих запросах. Следующие запросы после первого могут быть асинхронными.

person despot    schedule 24.10.2012

Просто сохраните свой файл 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();
person isaacselement    schedule 19.10.2013

Я заметил, что это происходит, когда куки отключены в ...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<property name="enableCookies" value="false"/>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"/>, чтобы сохранить идентификатор сеанса от того же соединения.

person Ernestas Gruodis    schedule 12.10.2015