web-dev-qa-db-fra.com

Pourquoi getSession () ne retourne-t-il pas la même session lors de requêtes ultérieures dans de courtes périodes?

J'envoie une demande $.getJSON (HTTP GET) deux fois (avec des données différentes), l'une après l'autre (disons que nous avons request1 et request2). Je peux voir dans les outils de développement de FF et de Chrome que j'ai le même champ d'en-tête cookie:JSESSIONID=FD0D502635EEB67E3D36203E26CBB59A.

Du côté du serveur, j'essaie d'obtenir la session:

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

Si j'imprime ces variables pour les deux demandes que je reçois,
request1:

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

request2:

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

Comme vous pouvez le constater, le serveur a clairement créé une nouvelle session pour request2 sur request.getSession(). Mais pourquoi fait-il cela? Il devrait théoriquement être synchronisé et vous donner la même session que celle créée par la première demande (qui a atteint ce code en premier). Maintenant, pour être sûr que la création de session est synchronisée, j'ai fait ce qui suit:

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

et j'ai eu les mêmes résultats.

Si j'envoie les mêmes demandes plus tard encore (disons request1 'et request2'), je reçois,
request1 ':

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

request2 ':

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

Si vous voyez maintenant de près, l'identifiant de session est le même (dans request1 'et request2') et est le dernier créé à partir de request2. Existe-t-il un moyen d’obtenir la même session à partir de plusieurs demandes ultérieures qui parviennent au serveur dans des délais très brefs?

Je n'utilise aucune fonctionnalité particulière - j'utilise la stratégie de session standard de Spring. De plus, il semble que le cookie JSESSIONID des 2 premières demandes (request1 et request2) proviennent de la première fois que je visite la page (disons qu'une requête0 a été envoyée au serveur lors de la création de ce JSESSIONID). Mais il semble également que, sauf si vous appelez explicitement request.getSession (), le serveur/serveur crée toujours un nouveau JSESSIONID pour chaque réponse et le renvoie au client. Ainsi, lorsqu'une nouvelle demande est envoyée par le client après une réponse, un nouveau JSESSIONID lui sera attribué. Il semble que la gestion des sessions Spring out of the Box ne fonctionne pas correctement.

Sincères amitiés,
despote

RECHERCHE ADDITIONNELLE :

Je voulais voir si je pouvais enregistrer la création de session avec un HttpSessionListner. Ainsi, je peux voir quand la session portant l'ID FD0D502635EEB67E3D36203E26CBB59A (le cookie en cours d'envoi dans request1 et request2) est créée. Et aussi, si vous utilisez l'écouteur (le SessionProcessor), je peux stocker les sessions sur une carte par identifiant et les récupérer ultérieurement par l'identifiant du cookie (je n'ai donc pas besoin de créer une autre session).
Alors voici le code:

public interface ISessionProcessor extends ISessionRetriever, ISessionPopulator {
}

public interface ISessionRetriever {

    HttpSession getSession(String sessionId);
}

public interface ISessionPopulator {

    HttpSession setSession(String sessionId, HttpSession session);
}

La raison de cette séparation était que je voulais seulement permettre à l’auditeur d’ajouter des sessions à la carte, et aux contrôleurs de ne pouvoir créer une session que via request.getSession (). La méthode sessionCreated du listner était donc toujours invoquée (comme voir ci-dessous).

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

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

dans my-servlet-context.xml:

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

dans mon contrôleur:

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

Cela m'a donné les mêmes résultats. Il est apparu que la création de session par un moyen autre que request.getSession (lorsque spring a créé la session) a été soit non enregistrée par l'auditeur, soit le cookie/jsessionID est venu ailleurs. Regardez la réponse pour plus.

Autres sources qui m'ont aidé à résoudre les problèmes de HttpSession:
injection de contexte de servlet dans le contrôleur
aperçu de la concurrence lorsque vous devez travailler avec HttpSession
utilisation de l'objet HttpSession pour effectuer la synchronisation (éviter ceci)
le "meilleur" moyen de synchronisation lors de l'utilisation de HttpSession
Quelques références de printemps:
gestion de session
gestion de session en sécurité
discussions sur la façon d’obtenir une session lorsque vous avez une sessionId (ce que j’ai fait ci-dessus):
discussion coderanch
stackoverflow
le message qui m'a aidé à finaliser le transfert automatique de mon auditeur

14
despot

il semble que le cookie JSESSIONID de la première 2 requêtes (request1 et request2) proviennent de la première fois que je visite la page (disons qu'une requête0 a été envoyée au serveur lors de sa création. JSESSIONID).

Ce n'était pas vrai. J'ai 2 applications déployées sous le même domaine sur le même serveur. Ainsi, lorsque j'ai appelé http://mydomain.com/app1/initpage le serveur a créé une session pour app1 avec l'ID FD0D502635EEB67E3D36203E26CBB59A et a envoyé ce JSESSIONID dans un cookie au client. Le client a enregistré le cookie sous mydomain.com et la deuxième fois que j'ai exécuté http://mydomain.com/app2/executeService , le navigateur du client a envoyé le JSESSIONID du cookie dans l'en-tête de la demande. Je l'ai reçu sur le serveur mais ce n'était pas une session dans l'autre app2. 

Cela explique le fait que lorsque j'envoie les deux autres requêtes (request1 'et request2'), un ID de session est créé sur l'application appropriée.

Regardez plus ici:
Déploiement de plusieurs applications Web sur le même serveur
Dans quelles conditions un JSESSIONID est-il créé?

Pour ce qui est de la réponse concrète à ma question, il apparaît que vous devez synchroniser la première demande afin de toujours être sûr que vous avez le même identifiant de session dans les demandes suivantes. Les requêtes suivantes après la première peuvent être asynchrones.

1
despot

Il vous suffit de stocker votre cookie (avec JESSIONID) dans le client. Lorsque vous envoyez une demande ultérieure au serveur, placez le cookie stocké dans le champ d’en-tête de votre demande et envoyez-le, puis vous obtiendrez la même session du côté serveur.

CLIENT (IOS) stocke votre cookie de la réponse:

    NSHTTPURLResponse* httpURLReqponse = (NSHTTPURLResponse*) response;
    NSDictionary* allHeaders = [httpURLReqponse allHeaderFields];
    NSLog(@"Response Headers : %@ ", allHeaders);
    NSString* cookie = [allHeaders objectForKey: @"Set-Cookie"];
    DATA.cookies = cookie;      // Store the cookie

CLIENT (IOS) envoyez votre demande suivante avec cookie:

// Put the stored cookie in your request header
[(NSMutableURLRequest*)request addValue: DATA.cookies forHTTPHeaderField:@"cookie"];
[NSURLConnection sendAsynchronousRequest: request queue:[NSOperationQueue mainQueue] completionHandler:nil];

Pas seulement pour le client IOS. Ensuite, du côté du serveur, vous obtiendrez la même session:

Serveur (JavaEE) GET HttpSession:

// Get the session, print its hashCode. You will find out that it's same as previous.
HttpSession session = ServletActionContext.getRequest().getSession();
0
isaacselement

J'ai remarqué que cela se produit lorsque les cookies sont désactivés dans le fichier ...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>

Il devrait s'agir de <property name="enableCookies" value="false"/> pour conserver l'ID de session de la même connexion.

0
Ernestas Gruodis