web-dev-qa-db-fra.com

Comment différencier les sessions dans les onglets du navigateur?

Dans une application Web implémentée dans Java à l'aide de JSP et de Servlets; Si je stocke des informations dans la session utilisateur, ces informations sont partagées à partir de tous les onglets du même navigateur. Comment différencier les sessions dans les onglets du navigateur? Dans cet exemple:

<%@page language="Java"%>
<%
String user = request.getParameter("user");
user = (user == null ? (String)session.getAttribute("SESSIONS_USER") : user);
session.setAttribute("SESSIONS_USER",user);
%>
<html><head></head><body>
<%=user %>
<form method="post">
User:<input name="user" value="">
<input type="submit" value="send">
</form>
</body></html>

Copiez ce code dans une page jsp (testpage.jsp), déployez ce fichier dans le contexte existant d’une application Web sur le serveur (j’utilise Apache Tomcat), puis ouvrez un navigateur (FF, IE7 ou Opera) en utilisant le fichier correct. URL (localhost/context1/testpage.jsp), saisissez votre nom et saisissez le formulaire. Ouvrez ensuite un nouvel onglet dans le même navigateur et vous pourrez voir votre nom (extrait de la session) sur le nouvel onglet. Faites attention avec le cache du navigateur, cela semble parfois ne pas se produire, mais c'est dans le cache, actualisez le deuxième onglet.

Merci.

130
Oriol Terradas

Vous pouvez utiliser HTML5 SessionStorage (window.sessionStorage). Vous allez générer un identifiant aléatoire et enregistrer dans la session Stockage par onglet du navigateur. Ensuite, chaque onglet du navigateur a son propre identifiant.

Les données stockées à l'aide de sessionStorage ne persistent pas dans les onglets du navigateur, même si deux onglets contiennent des pages Web du même domaine Origine. En d'autres termes, les données à l'intérieur de sessionStorage ne se limitent pas au domaine et au répertoire de la page appelante, mais également à l'onglet du navigateur dans lequel la page est contenue. Contrastez cela aux cookies de session, qui conservent les données d'un onglet à l'autre.

89
Gonzalo Gallotti

Vous devez comprendre que les sessions côté serveur sont un complément artificiel à HTTP. Étant donné que HTTP est sans état, le serveur doit reconnaître en quelque sorte qu'une demande appartient à un utilisateur particulier pour lequel il dispose d'une session. Il y a 2 façons de faire cela:

  • Biscuits. La méthode la plus propre et la plus populaire, mais cela signifie que tous les onglets et toutes les fenêtres du navigateur partagent la session. IMO, c’est en fait souhaitable, et je serais très ennuyé de voir un site qui me permettait de me connecter à chaque nouvel utiliser les onglets de manière très intensive
  • Réécriture d'URL. Un identifiant de session est ajouté à chaque URL du site. C'est plus de travail (vous devez faire quelque chose partout où vous avez un lien interne au site), mais il est possible d'avoir des sessions séparées dans différents onglets, bien que les onglets ouverts par le lien partagent toujours la session. Cela signifie également que l'utilisateur doit toujours se connecter lorsqu'il se rend sur votre site.

Qu'essayez-vous de faire de toute façon? Pourquoi voudriez-vous que les onglets aient des sessions séparées? Peut-être qu’il ya un moyen d’atteindre votre objectif sans utiliser de sessions du tout?

Edit: Pour le test, d'autres solutions peuvent être trouvées (telles que l'exécution de plusieurs instances de navigateur sur des ordinateurs virtuels distincts). Si un utilisateur doit agir simultanément dans différents rôles, le concept de "rôle" doit être géré dans l'application de sorte qu'un identifiant de connexion puisse avoir plusieurs rôles. Vous devrez décider si ceci, en utilisant la réécriture d'URL, ou simplement vivre avec la situation actuelle est plus acceptable, car il est tout simplement impossible de gérer les onglets du navigateur séparément avec des sessions basées sur des cookies.

22
Michael Borgwardt

Vous ne devriez pas. Si vous voulez faire cela, vous devez obliger l'utilisateur à utiliser une seule instance de votre application en écrivant des URL à la volée et en utilisant un identifiant sessionID identique (pas sessionid, il l'a gagné). t work) id et le transmettre dans chaque URL.

Je ne sais pas pourquoi vous en avez besoin, mais à moins que vous n'ayez besoin de faire une application totalement inutilisable, ne le faites pas.

17
dr. evil

La propriété Javascript window.name est la seule chose qui persiste dans l’activité des onglets, mais qui peut rester indépendante (au lieu de guff d’URL).

16
Dave Bish

Je suis venu avec une nouvelle solution, qui a un peu de frais généraux, mais semble fonctionner jusqu'à présent comme un prototype. Une hypothèse est que vous vous connectez dans un environnement système d'honneur, bien que cela puisse être adapté en recherchant un mot de passe à chaque changement d'onglet.

Utilisez localStorage (ou l’équivalent) et l’événement de stockage HTML5 pour détecter quand un nouvel onglet de navigateur a basculé quel utilisateur est actif. Lorsque cela se produit, créez une superposition fantôme avec un message indiquant que vous ne pouvez pas utiliser la fenêtre en cours (ou désactivez temporairement la fenêtre, vous voudrez peut-être que ce ne soit pas aussi visible.) Lorsque la fenêtre reprend le focus, envoyez un AJAX request reconnexion de l'utilisateur.

Une mise en garde à cette approche: vous ne pouvez pas avoir d'appels normaux AJAX (c.-à-d. Ceux qui dépendent de votre session) se déroulent dans une fenêtre qui n'a pas le focus (par exemple, si vous avez un appel passé après un délai), à moins que vous ne fassiez manuellement un appel AJAX avant de vous reconnecter. Donc, tout ce dont vous avez besoin est de faire vérifier votre fonction AJAX pour vous assurer que localStorage.currently_logged_in_user_id === window.yourAppNameSpace.user_id et sinon, connectez-vous d'abord via AJAX.

Les conditions de concurrence en sont un autre exemple: si vous pouvez changer de fenêtre assez rapidement pour la rendre confuse, vous risquez de vous retrouver avec une séquence relogin1-> relogin2-> ajax1-> ajax2, avec ajax1 créé sous la mauvaise session. Contournez ce problème en transférant les demandes de login AJAX sur un tableau, puis annulez toutes les requêtes en cours avant de lancer une nouvelle demande de connexion.

La dernière chose à surveiller est le rafraîchissement de la fenêtre. Si quelqu'un actualise la fenêtre alors que vous avez une demande de connexion AJAX active mais non terminée, le nom de la mauvaise personne sera actualisé. Dans ce cas, vous pouvez utiliser l'événement beforeunload non standard pour avertir l'utilisateur du risque de confusion et lui demander de cliquer sur Annuler tout en émettant de nouveau une demande de connexion AJAX. Ensuite, le seul moyen de le faire est de cliquer sur OK avant la fin de la demande (ou de frapper accidentellement sur entrée/barre d'espace, car OK est - malheureusement pour ce cas-ci, la valeur par défaut.) Il existe d'autres moyens de gérer ce cas, comme détecter les pressions F5 et Ctrl + R/Alt + R, ce qui fonctionnera dans la plupart des cas mais pourrait être contrecarré par la reconfiguration des raccourcis clavier de l'utilisateur ou l'utilisation d'un autre système d'exploitation. Cependant, c’est un peu un cas d’Edge en réalité, et les pires scénarios ne sont jamais aussi graves: dans une configuration de système honoraire, vous seriez connecté en tant que mauvaise personne (mais vous pouvez faire comprendre que c’est la cas en personnalisant les pages avec des couleurs, des styles, des noms bien en évidence, etc.); dans une configuration de mot de passe, il incombe à la dernière personne qui a entré son mot de passe de se déconnecter ou de partager sa session ou, si cette personne est en fait l'utilisateur actuel, il n'y a pas d'infraction.

Mais au final, vous avez une application avec un utilisateur par onglet qui (espérons-le) agit comme il se doit, sans avoir à configurer des profils, à utiliser IE ou à réécrire des URL. Assurez-vous d'indiquer clairement dans chaque onglet qui est connecté à cet onglet en particulier ...

13
Kev

Nous avons eu ce problème et nous l'avons résolu très facilement. Je veux dire facile parce qu'aucune programmation n'est impliquée. Ce que nous voulions faire était de laisser un utilisateur se connecter à plusieurs comptes dans la même fenêtre de navigateur sans créer de conflit entre les sessions.

La solution était donc des sous-domaines aléatoires.

23423.abc.com
242234.abc.com
235643.abc.com

Nous avons donc demandé à notre administrateur système de configurer les certificats SSL pour * .abc.com plutôt que abc.com. Alors avec peu de changement de code, chaque fois qu'un utilisateur essaie de se connecter, il est connecté dans un onglet avec un numéro de sous-domaine aléatoire. afin que chaque onglet puisse avoir sa propre session indépendamment. Aussi, pour éviter tout conflit, nous avons développé le nombre aléatoire en utilisant un hash ou md5 d’identifiant utilisateur.

10
user3534653

Je serai honnête ici. . .tout ci-dessus peut être ou ne pas être vrai, mais tout semble MANIÈRE trop compliqué, ou ne répond pas en sachant quel onglet est utilisé côté serveur.

Parfois, nous devons appliquer le rasoir d'Occam.

Voici l'approche d'Occam: (non, je ne suis pas Occam, il est mort en 1347)

1) attribuer un identifiant unique du navigateur à votre page lors du chargement. . . si et seulement si la fenêtre n'a pas encore d'identifiant (utilisez donc un préfixe et une détection)

2) sur chaque page que vous avez (utilisez un fichier global ou quelque chose de ce genre), mettez simplement du code en place pour détecter l’événement de focus et/ou le survol de la souris. (J'utiliserai jquery pour cette partie, pour faciliter l'écriture de code)

3) dans votre fonction focus (et/ou mouseover), définissez un cookie contenant le nom window.name

4) lisez cette valeur de cookie côté serveur lorsque vous devez lire/écrire des données spécifiques à un onglet.

Côté client:

//Events 
$(window).ready(function() {generateWindowID()});
$(window).focus(function() {setAppId()});
$(window).mouseover(function() {setAppId()});


function generateWindowID()
{
    //first see if the name is already set, if not, set it.
    if (se_appframe().name.indexOf("SEAppId") == -1){
            "window.name = 'SEAppId' + (new Date()).getTime()
    }
    setAppId()
}

function setAppId()
{
    //generate the cookie
    strCookie = 'seAppId=' + se_appframe().name + ';';
    strCookie += ' path=/';

    if (window.location.protocol.toLowerCase() == 'https:'){
        strCookie += ' secure;';
    }

    document.cookie = strCookie;
}

côté serveur (C # - à titre d'exemple)

//variable name 
string varname = "";
HttpCookie aCookie = Request.Cookies["seAppId"];
if(aCookie != null) {
     varname  = Request.Cookies["seAppId"].Value + "_";
}
varname += "_mySessionVariable";

//write session data 
Session[varname] = "ABC123";

//readsession data 
String myVariable = Session[varname];

Terminé.

4
Mike A.

En javascript, comment puis-je identifier de manière unique une fenêtre de navigateur parmi une autre qui se trouvent sous le même sessionId cookiedbased

Utilisez essentiellement window.name. S'il n'est pas défini, définissez-le sur une valeur unique et utilisez-le. Ce sera différent selon les onglets appartenant à la même session.

2
akkishore

Je pense que ce que vous voulez probablement est de maintenir l’état de navigation entre les onglets et non de créer spécifiquement une seule session par onglet. C’est exactement ce que le framework Seam réalise avec son périmètre/contexte de conversation. Leur implémentation repose sur le fait qu’un identifiant de conversation est propagé à chaque requête et crée la notion de conversation côté serveur, qui se situe entre une session et une requête. Il permet le contrôle du flux de navigation et la gestion des états.

Bien qu’il s’agisse principalement de JSF, jetez un œil et vérifiez si vous pouvez en tirer quelques idées: http://docs.jboss.org/seam/latest/reference/en-US/html_single/# d0e362

2
lsoliveira

Une autre approche qui fonctionne consiste à créer un identifiant de fenêtre unique et à stocker cette valeur avec l'identifiant de session dans une table de base de données. L'identifiant de la fenêtre que j'utilise souvent est un entier (maintenant). Cette valeur est créée à l'ouverture d'une fenêtre et réaffectée à la même fenêtre si celle-ci est actualisée, rechargée ou soumise à elle-même. Les valeurs de fenêtre (entrées) sont enregistrées dans la table locale à l'aide du lien. Lorsqu'une valeur est requise, elle est obtenue à partir de la table de base de données en fonction du lien id Windows/ID de session. Bien que cette approche nécessite une base de données locale, elle est pratiquement infaillible. L'utilisation d'une table de base de données était facile pour moi, mais je ne vois aucune raison pour que les tableaux locaux ne fonctionnent pas aussi bien.

1
Stephen

Vous pouvez utiliser la réécriture de liens pour ajouter un identifiant unique à toutes vos URL lorsque vous démarrez sur une seule page (par exemple, index.html/jsp/what). Le navigateur utilisera les mêmes cookies pour tous vos onglets, de sorte que tout ce que vous insérerez dans les cookies sera non unique.

1
Bombe

La session de printemps prend en charge plusieurs sessions dans le même navigateur Consultez les exemples et les détails de la mise en œuvre http://docs.spring.io/spring-session/docs/current/reference/html5/guides/users.html

1
vasa.v03

J'ai récemment développé une solution à ce problème en utilisant des cookies. Voici un lien vers ma solution. J'ai également inclus un exemple de code de la solution utilisant ASP.NET. Vous devriez pouvoir l'adapter à JSP ou à Servelets si vous en avez besoin.

https://sites.google.com/site/sarittechworld/track-client-windows

1
Sarit Kommineni

Je vois de nombreuses implémentations qui ont des modifications côté client pour manipuler les cookies d'identification de session. Mais en général, les cookies d'identification de session doivent être HttpOnly pour que le script Java ne puisse pas accéder, sans quoi cela pourrait conduire à un détournement de session via XSS.

0
vasa.v03

Si c'est parce que chaque onglet exécute un flux différent dans votre application et que le mélange des deux flux pose des problèmes, il est préférable de "Régionaliser" vos objets de session afin que chaque flux utilise une région différente de la session.

Cette région peut être implémentée simplement en ayant différents préfixes pour chaque flux, ou un objet de session contiendra plusieurs mappes (une pour chaque flux), et vous utiliserez ces mappes au lieu d'attributs de session. Le mieux serait d'étendre votre classe de session. utilisez-le à la place.

0
hussein.g

Stocker timeStamp dans window.sessionStorage s'il n'est pas déjà défini. Cela donnera une valeur unique pour chaque onglet (même si les URL sont identiques)

http://www.javascriptkit.com/javatutors/domstorage.shtml

https://developer.mozilla.org/en-US/docs/Web/Guide/API/DOM/Storage

J'espère que cela t'aides.

0
guest

vous devrez faire

1- stocker un cookie pour la liste des comptes

2- en option stocker un cookie pour celui par défaut

3- stocker pour chaque compte avec son index comme acc1, acc2

4- mettez dans l'url quelque chose représente l'index des comptes et sinon vous sélectionnerez celui par défaut comme google mail domain.com/0/some-url >> 0 représente l'index du compte et vous devrez peut-être savoir comment utiliser urlwrite

5- Lorsque vous sélectionnez un cookie, sélectionnez-le en fonction de votre chemin url, qui représente l'index du compte.

Cordialement

0
Mhmd

Remarque: La solution ici doit être faite au stade de la conception de l'application. Il serait difficile de concevoir cela plus tard.

Utilisez un champ masqué pour transmettre l'identifiant de session .

Pour que cela fonctionne, chaque page doit inclure un formulaire:

<form method="post" action="/handler">

  <input type="hidden" name="sessionId" value="123456890123456890ABCDEF01" />
  <input type="hidden" name="action" value="" />

</form>

Chaque action de votre côté, y compris la navigation, renvoie le formulaire (en définissant la action comme il convient). Pour les demandes "non sécurisées" , vous pouvez inclure un autre paramètre, par exemple contenant une valeur JSON des données à soumettre:

<input type="hidden" name="action" value="completeCheckout" />
<input type="hidden" name="data" value='{ "cardNumber" : "4111111111111111", ... ' />

Comme il n'y a pas de cookies, chaque onglet sera indépendant et n'aura aucune connaissance des autres sessions du même navigateur.

De nombreux avantages, notamment en matière de sécurité:

  • Aucune dépendance sur JavaScript ou HTML5.
  • Protège de manière inhérente contre CSRF .
  • Ne vous fiez pas aux cookies, protégez-vous contre CANICHE .
  • Non vulnérable à fixation de session .
  • Peut empêcher l'utilisation du bouton Précédent, ce qui est souhaitable lorsque vous souhaitez que les utilisateurs suivent un chemin défini sur votre site (ce qui signifie que les bogues logiques qui peuvent parfois être attaqués par des demandes hors service peuvent être évités).

Quelques inconvénients:

  • La fonctionnalité du bouton Précédent peut être souhaitée.
  • Pas très efficace avec la mise en cache car chaque action est un POST.

Plus d'informations ici .

0
SilverlightFox

J'ai lu ce post parce que je pensais que je voulais faire la même chose. J'ai une situation similaire pour une application sur laquelle je travaille. Et, en réalité, il s’agit de tester plus que de la praticité.

Après avoir lu ces réponses, en particulier celle de Michael Borgwardt, j'ai réalisé le flux de travail qui doit exister:

  1. Si l'utilisateur accède à l'écran de connexion, recherchez une session existante. S'il en existe un, ignorez l'écran de connexion et envoyez-le à l'écran de bienvenue.
  2. Si l'utilisateur (dans mon cas) accède à l'écran d'inscription, recherchez une session existante. S'il en existe un, indiquez à l'utilisateur que vous allez déconnecter cette session. S'ils sont d'accord, déconnectez-vous et commencez l'inscription.

Cela résoudra le problème de voir les données d'un "autre utilisateur" dans leur session. Ils ne voient pas vraiment les données d'un "autre utilisateur" dans leur session, ils voient vraiment les données de la seule session ouverte. Clairement, cela cause certaines données intéressantes, car certaines opérations écrasent certaines données de session et pas d'autres, de sorte que vous disposez d'une combinaison de données dans cette session.

Maintenant, pour aborder la question des tests. La seule approche viable consisterait à utiliser directives du préprocesseur pour déterminer si des sessions sans cookies doivent être utilisées. Vous voyez, en construisant une configuration spécifique pour un environnement spécifique, je suis en mesure de formuler des hypothèses sur l'environnement et sur son utilisation. Cela me permettrait techniquement d'avoir deux utilisateurs connectés en même temps et que le testeur puisse tester plusieurs scénarios à partir de la même session de navigateur sans jamais se déconnecter d'aucune de ces sessions de serveur.

Cependant, cette approche présente de sérieuses mises en garde. Notamment, le testeur teste et non ce qui sera exécuté en production.

Je pense donc que je dois dire que c’est finalement une mauvaise idée .

0
Mike Perrenoud

J'ai résolu ceci de manière suivante:

  • J'ai attribué un nom à la fenêtre. Ce nom est identique à celui de la ressource de connexion.
  • plus 1 pour supprimer stocké dans le cookie pour attacher la connexion.
  • J'ai créé une fonction qui capture toutes les réponses xmloutput, assigne sid et supprime les cookies au format json. Je le fais pour chaque window.name.

voici le code:

var deferred = $q.defer(),
        self = this,
        onConnect = function(status){
          if (status === Strophe.Status.CONNECTING) {
            deferred.notify({status: 'connecting'});
          } else if (status === Strophe.Status.CONNFAIL) {
            self.connected = false;
            deferred.notify({status: 'fail'});
          } else if (status === Strophe.Status.DISCONNECTING) {
            deferred.notify({status: 'disconnecting'});
          } else if (status === Strophe.Status.DISCONNECTED) {
            self.connected = false;
            deferred.notify({status: 'disconnected'});
          } else if (status === Strophe.Status.CONNECTED) {
            self.connection.send($pres().tree());
            self.connected = true;
            deferred.resolve({status: 'connected'});
          } else if (status === Strophe.Status.ATTACHED) {
            deferred.resolve({status: 'attached'});
            self.connected = true;
          }
        },
        output = function(data){
          if (self.connected){
            var rid = $(data).attr('rid'),
                sid = $(data).attr('sid'),
                storage = {};

            if (localStorageService.cookie.get('day_bind')){
              storage = localStorageService.cookie.get('day_bind');
            }else{
              storage = {};
            }
            storage[$window.name] = sid + '-' + rid;
            localStorageService.cookie.set('day_bind', angular.toJson(storage));
          }
        };
    if ($window.name){
      var storage = localStorageService.cookie.get('day_bind'),
          value = storage[$window.name].split('-')
          sid = value[0],
          rid = value[1];
      self.connection = new Strophe.Connection(BoshService);
      self.connection.xmlOutput = output;
      self.connection.attach('bosh@' + BoshDomain + '/' + $window.name, sid, parseInt(rid, 10) + 1, onConnect);
    }else{
      $window.name = 'web_' + (new Date()).getTime();
      self.connection = new Strophe.Connection(BoshService);
      self.connection.xmlOutput = output;
      self.connection.connect('bosh@' + BoshDomain + '/' + $window.name, '123456', onConnect);
    }

J'espère t'aider

0
German Sanchez
How to differ sessions in browser-tabs?

Le moyen le plus simple de différer les sessions dans les onglets du navigateur est d'interdire à votre domaine particulier de définir des cookies. De cette façon, vous pouvez avoir des sessions séparées à partir d'onglets séparés. Supprimez les cookies de ce domaine: www.xyz.com. Vous ouvrez l'onglet 1, connectez-vous et commencez à naviguer. Ensuite, vous ouvrez l'onglet 2 et vous pouvez vous connecter en tant que même utilisateur ou différent; de toute façon, vous aurez une session séparée de l’onglet 1. Et ainsi de suite.

Mais bien sûr, cela est possible lorsque vous avez le contrôle du côté client. Sinon, les solutions prescrites par les gens ici devraient s'appliquer.

0
Kingz