Comment différencier les sessions dans les onglets de navigateur?
dans une application web implémentée en java en utilisant JSP et Servlets; si je stocke des informations dans la session de l'utilisateur, ces informations sont partagées à partir de tous les onglets du même navigateur. Comment diffèrent des séances dans le navigateur onglets? 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>
copier ce code dans une page jsp ( testpage.jsp
), déployer ce fichier dans un contexte existant d'une application web sur le serveur( J'utilise Apache Tomcat), puis ouvrir un navigateur (FF, IE7 ou Opera) en utilisant le bon URL ( localhost/context1/testpage.jsp
), Tapez votre nom dans la saisie et soumettez le formulaire. Ensuite, ouvrez un nouvel onglet dans le navigateur, et vous pouvez voir votre nom (obtenir à partir de la session) sur le nouvel onglet. Soyez prudent avec le navigateur-cache, semble parfois que cela ne se produit pas, mais il est dans le cache, rafraîchir le deuxième onglet.
Merci.
21 réponses
vous pouvez utiliser HTML5 SessionStorage (window.sessionStorage). Vous générerez un ID aléatoire et enregistrerez dans le stockage de session par onglet de navigateur. Ensuite, chaque onglet de navigateur a son propre Id.
données stockées en utilisant sessionStorage ne persistent pas à travers les onglets du navigateur, même si deux onglets contiennent des pages Web de la même origine de domaine. Dans en d'autres termes, les données à l'intérieur de sessionStorage ne se limitent pas domaine et répertoire de la page d'invocation, mais le onglet de navigateur dans dont la page est contenue dans. Contraste avec les cookies de session, qui persistent données d'un onglet à l'autre.
vous devez réaliser que les sessions côté serveur sont un add-on artificiel à HTTP. Comme HTTP est apatride, le serveur doit d'une manière ou d'une autre reconnaître qu'une requête appartient à un utilisateur particulier qu'il connaît et pour lequel il dispose d'une session. Il y a 2 façons de le faire:
- Cookies. La méthode plus propre et plus populaire, mais cela signifie que tous les onglets et fenêtres de navigateur par un utilisateur partagent la session-IMO cela est en fait souhaitable, et je serais très ennuyé à un site cela m'a fait me connecter pour chaque nouvel onglet, puisque j'utilise les onglets très intensivement
- la réécriture d'URL. Toute URL sur le site a un ID de session en annexe. Ceci est plus de travail (vous devez faire quelque chose partout où vous avez un site-Lien interne), mais rend possible d'avoir des sessions séparées dans différents onglets, bien que les onglets ouverts par le lien partageront toujours la session. Cela signifie également que l'utilisateur doit toujours se connecter quand il vient à votre site.
What tu essaies de le faire de toute façon? Pourquoi voudriez-vous que les onglets aient des sessions séparées? Il existe peut-être une façon d'atteindre votre objectif sans l'aide de sessions?
Edit: Pour les tests, d'autres solutions peuvent être trouvées (telles que l'exécution de plusieurs instances de navigateur sur des VMs séparés). Si un utilisateur doit agir dans des rôles différents en même temps, alors le concept de "rôle" doit être manipulé dans l'application afin qu'un login puisse avoir plusieurs rôles. Vous aurez à décider que ce soit en utilisant la réécriture D'URL, ou simplement en vivant avec la situation actuelle est plus acceptable, parce qu'il n'est tout simplement pas possible de gérer les onglets de navigateur séparément avec des sessions basées sur les cookies.
la fenêtre.la propriété Nom Javascript, est la seule chose qui persistera dans l'activité de l'onglet, mais peut rester indépendante (au lieu de guff URL).
vous ne devriez pas. si vous voulez faire une telle chose, soit vous devez forcer l'utilisateur à utiliser une seule instance de votre application en écrivant des URLs à la volée, utilisez un id de type sessionID (pas sessionid il ne fonctionnera pas) et passez-le dans chaque URL.
Je ne sais pas pourquoi vous en avez besoin mais à moins que vous ayez besoin de faire une application totalement inutilisable ne le faites pas.
j'ai trouvé une nouvelle solution, qui a un petit morceau de plafond, mais semble travailler jusqu'à présent comme un prototype. Une hypothèse est que vous êtes dans un environnement de système d'honneur pour vous connecter, bien que cela puisse être adapté en demandant un mot de passe chaque fois que vous changez les onglets.
utilisez localStorage (ou l'équivalent) et l'événement de stockage HTML5 pour détecter quand un nouvel onglet de navigateur a changé quel utilisateur est actif. Lorsque cela se produit, créer une superposition fantôme avec un message disant que vous ne pouvez pas utiliser la fenêtre courante (ou autrement désactiver la fenêtre temporairement, vous pourriez ne pas vouloir qu'elle soit aussi visible.) Lorsque la fenêtre reprend la mise au point, envoyez une requête AJAX qui enregistre l'utilisateur.
une mise en garde à cette approche: vous ne pouvez pas avoir d'appels AJAX normaux (i.e., ceux qui dépendent de votre session) qui se produisent dans une fenêtre qui n'a pas le focus (par exemple si vous avez eu un appel se produisant après un retard), à moins que vous ne fassiez manuellement un appel de re-login AJAX avant que. Donc vraiment tout ce que vous devez faire est d'avoir votre fonction AJAX vérifier d'abord pour être sûr localStorage.currently_logged_in_user_id === fenêtre.yourAppNameSpace.user_id, et si ce n'est pas le cas, connectez-vous d'abord via AJAX.
un autre est les conditions de course: si vous pouvez changer de windows assez rapidement pour le confondre, vous pouvez finir avec une relogin1->relogin2->ajax1->ajax2 séquence, avec ajax1 étant fait sous la mauvaise session. Travaillez autour de cela en poussant les requêtes AJAX de connexion sur un tableau, et puis onstorage et avant d'émettre une nouvelle demande de connexion, annulez toutes les demandes en cours.
La dernière chasse aux sorcières à regarder dehors pour est fenêtre s'actualise. Si quelqu'un rafraîchit la fenêtre pendant que vous avez une demande de connexion AJAX active mais non terminée, elle sera rafraîchie au nom de la mauvaise personne. Dans ce cas, vous pouvez utiliser l'événement nonstandard beforeunload pour avertir l'utilisateur de la confusion potentielle et lui demander de cliquer sur Annuler, tout en réémettant une demande de connexion AJAX. Puis le la seule façon de le faire est de cliquer sur OK avant que la requête ne soit terminée (ou de frapper accidentellement enter/spacebar, parce que OK est--malheureusement pour ce cas--la valeur par défaut.) Il existe d'autres moyens de gérer ce cas, comme la détection des presses F5 et Ctrl+R/Alt+R, qui fonctionneront dans la plupart des cas, mais pourraient être contrecarrés par une reconfiguration par raccourci du clavier utilisateur ou par une utilisation alternative du système D'exploitation. Toutefois, il s'agit d'un cas un peu extrême dans la réalité, et les pires scénarios ne sont jamais aussi mauvais: dans un honneur configuration du système, vous seriez connecté comme la mauvaise personne (mais vous pouvez rendre évident que c'est le cas en personnalisant les pages avec des couleurs, des styles, des noms affichés 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 courant, alors il n'y a pas de violation.
mais à la fin vous avez une application d'un utilisateur par onglet que (espérons) agit simplement comme il se doit, sans avoir à nécessairement configurer des profils, utiliser IE, ou réécrire des URLs. Assurez-vous que vous le faites évident dans chaque onglet qui est connecté dans cet onglet particulier, cependant...
nous avions ce problème et nous l'avons résolu très facilement. Je veux dire facile, car aucune programmation. Ce que nous voulions faire était de permettre à un utilisateur de se connecter à plusieurs comptes dans la même fenêtre de navigateur sans entrer en conflit avec les sessions.
donc la solution était 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 abc.com Ensuite, avec peu de changement de code, chaque fois qu'un utilisateur essaie de se connecter, il est enregistré dans un onglet avec un numéro de sous-domaine aléatoire. ainsi chaque onglet pourrait avoir sa propre session indépendamment. Aussi pour éviter tout conflit, nous avons développé le nombre aléatoire en utilisant un hachage ou md5 de user id.
je vais être honnête. . . tout ci-dessus peut ou peut ne pas être vrai, mais il semble que CHEMIN trop compliqué, ou n'a pas d'adresse sachant ce que l'onglet est utilisé côté serveur.
il faut parfois appliquer le rasoir D'Occam.
Voici l'approche de L'Occam: (Non, Je ne suis pas Occam, il est mort en 1347)
1) assignez un identifiant unique de navigateur à votre page sur charge. . . si et seulement si la fenêtre n'a pas un id (pour l'utilisation d'un préfixe et d'un système de détection)
2) sur chaque page que vous avez (utilisez un fichier global ou quelque chose) mettez simplement le code en place pour détecter l'événement focus et/ou l'événement mouseover. (Je vais utiliser jquery pour cette partie, pour la facilité de la rédaction de code)
3) dans votre fonction focus (et/ou mouseover), définissez un cookie avec la fenêtre.nom dedans
4) lisez cette valeur de cookie de votre serveur côté lorsque vous avez besoin de lecture/écriture de l'onglet données.
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 (en C# pour les fins de l'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];
fait.
je pense que ce que vous voulez probablement est de maintenir l'état de navigation à travers les onglets et ne pas créer spécifiquement une seule session par onglet. C'est exactement ce que le cadre de couture réalise avec leur portée/contexte de Conversation. Leur mise en œuvre repose sur le fait qu'un ID de conversation est propagé avec chaque requête et crée la notion d'une conversation du côté du serveur, qui est quelque chose qui se situe entre une session et une requête. Il permet le contrôle du flux de navigation et l'état gestion.
bien que ce soit principalement destiné à JSF, jetez un oeil et vérifiez si c'est quelque chose où vous pouvez prendre quelques idées de: http://docs.jboss.org/seam/latest/reference/en-US/html_single/#d0e3620
utilise essentiellement la fenêtre.nom. Si elle n'est pas définie, définissez-la à une valeur unique et utilisez-la. Il sera différente entre les onglets qui appartiennent à la même session.
vous pouvez utiliser la réécriture de lien pour ajouter un identifiant unique à toutes vos URLs en commençant par une seule page (par exemple index.html / JSP / whatever). Le navigateur utilisera les mêmes cookies pour tous vos onglets de sorte que tout ce que vous mettez dans les cookies sera pas être unique.
j'ai développé une solution à ce problème récemment en utilisant des Cookies. Voici un lien vers ma solution. J'ai également inclus le code échantillon de la solution en utilisant ASP.NET, vous devriez être en mesure de l'adapter à JSP ou Servelets si vous avez besoin.
https://sites.google.com/site/sarittechworld/track-client-windows
session de printemps prend en charge plusieurs sessions dans le même navigateur Examiner les échantillons et les détails de la mise en œuvre http://docs.spring.io/spring-session/docs/current/reference/html5/guides/users.html
une autre approche qui fonctionne est de créer un ID de fenêtre unique et de stocker cette valeur avec l'id de session dans une table de base de données. L'id de fenêtre que j'utilise souvent est entier(maintenant). Cette valeur est créée lorsqu'une fenêtre est ouverte et ré-affecté à la fenêtre même si la fenêtre est actualisé, rechargés ou soumis à lui-même. Les valeurs de fenêtre (entrées) sont sauvegardées dans la table locale en utilisant le lien. Lorsqu'une valeur est requise, elle est obtenue à partir de la table de la base de données basée sur l'id de la fenêtre / session id de lien. 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 laquelle les tableaux locaux ne fonctionneraient pas aussi bien.
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 l'aspect pratique.
après avoir lu ces réponses, en particulier celle de Michael Borgwardt, j'ai réalisé le flux de travail qui doit exister:
- si l'utilisateur navigue vers l'écran de connexion, vérifiez si une session existe. Si il en existe un bypass l'écran de connexion et de les envoyer à l'écran de bienvenue.
- Si l'utilisateur (dans mon cas) permet d'accéder à l'écran d'inscription, vérifier une session existante. S'il y en a un, faites savoir à 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 l'utilisateur 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 sont vraiment voir les données de la seule session qu'ils ont ouverte. Il est clair que cela provoque des données intéressantes car certaines opérations écrasent certaines données de session et pas d'autres de sorte que vous avez une combinaison de données dans cette session unique.
maintenant, pour régler la question des tests. La seule approche viable serait de tirer parti des "directives du préprocesseur pour déterminer si des sessions sans témoins devraient être utilisées. Voir, par construction dans une configuration spécifique pour un environnement je suis en mesure de faire certaines hypothèses sur l'environnement et ce à quoi il sert. Cela me permettrait techniquement d'avoir deux utilisateurs connectés en même temps et le testeur pourrait tester plusieurs scénarios de la même session de navigateur sans jamais se déconnecter de l'une de ces sessions de serveur.
Toutefois, cette approche comporte de sérieuses réserves. Pas le moindre, est le fait que ce que le testeur test est pas ce qui va courir dans production.
donc je pense que je dois dire, c'est finalement une mauvaise idée .
stockant l'horodatage dans la fenêtre.sessionStorage si elle n'est pas déjà défini. Cela donnera une valeur unique pour chaque onglet(même si les URLs sont les mêmes)
http://www.javascriptkit.com/javatutors/domstorage.shtml
https://developer.mozilla.org/en-US/docs/Web/Guide/API/DOM/Storage
Espérons que cette aide.
How to differ sessions in browser-tabs?
la façon la plus simple de différencier les sessions dans les onglets du navigateur est de ne pas autoriser votre domaine particulier à configurer les cookies. De cette façon, vous pouvez avoir des sessions séparées des onglets séparés. Disons que vous interdisez 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 soit à un même utilisateur ou un autre, de toute façon, vous aurez une session distincte de l'Onglet 1. Et ainsi de suite.
mais bien sûr cela est possible lorsque vous avez le contrôle sur le côté client. Sinon, les solutions prescrites par les gens ici devraient s'appliquer.
vous aurez besoin de faire
1 - stocker un cookie pour la liste des comptes
2 - en option stocker un cookie pour défaut un
3 - pour chaque compte avec son index, comme acc1, acc2
4 - mettre dans l'url quelque chose de représenter l'index des comptes et si non vous allez sélectionner celui par défaut comme google mail domain.com/0/some-url > > 0 ici représentent l'indice de Compte vous pouvez également avoir besoin de savoir comment utiliser urlwrite
5-lorsque vous sélectionnez un cookie, sélectionnez-le en fonction de votre urlpath représenter l'index du compte
concerne
je vois de nombreuses implémentations qui ont des modifications côté client pour manipuler les cookies d'id de session. Mais en général les cookies d'id de session doivent être HttpOnly de sorte que java-script ne peut pas accéder autrement il peut conduire à un détournement de Session à travers XSS
Note: la solution doit être trouvée au stade de la conception de l'application. Il serait difficile de concevoir cela plus tard.
utilisez un champ caché pour passer l'Identificateur 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>
toute action sur votre côté, y compris la navigation, affiche le formulaire de retour (réglage du action
selon le cas). Au lieu de "unsafe " requêtes, 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 dans le même navigateur.
beaucoup d'avantages, notamment en matière de sécurité:
- aucune confiance en JavaScript ou HTML5.
- intrinsèquement protège contre CSRF .
- Pas de confiance sur les cookies, donc protège contre le CANICHE .
- Non vulnérables fixation de session .
- peut empêcher l'utilisation du bouton arrière, ce qui est souhaitable lorsque vous voulez que les utilisateurs suivent un chemin défini à travers votre site (ce qui signifie que les bogues logiques qui peuvent parfois être attaqués par des requêtes hors-service, peuvent être évités).
Certains inconvénients:
- la fonctionnalité de bouton arrière peut être souhaitée.
- pas très efficace avec la mise en cache car chaque action est un POST.
j'ai résolu ceci de la manière suivante:
- j'ai assigné un nom à la fenêtre ce nom est le même que celui de la ressource de connexion.
- plus 1 pour rid stocké dans le cookie pour la connexion d'attache.
- j'ai créé une fonction pour capturer toutes les réponses xmloutput et assigner sid et rid à cookie au format json. Je fais ça pour chaque fenêtre.nom.
ici 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);
}
, je l'espère, vous aider à
si c'est parce que chaque onglet exécutera un flux différent dans votre application, et que le mélange des deux flux cause des problèmes, alors il est préférable de "Régionaliser" vos objets de session, de sorte que chaque flux utilisera une région différente de la session
cette région peut être implémentée aussi simplement qu'avoir des préfixes différents pour chaque flux, ou que l'objet session contiendra plusieurs cartes (une pour chaque flux), et vous utilisez ces cartes au lieu des attributs de session, le mieux étant serait de prolonger votre séance de classe et de l'utiliser à la place.