web-dev-qa-db-fra.com

Empêcher les personnes d'avoir mon site Web chargé sur plusieurs onglets

Je souhaite que les utilisateurs naviguent sur mon site à partir de only un onglet de leur navigateur. Comment cela peut-il être fait? Est-ce que j'utiliserais du javascript et des cookies? 

Par exemple, j'ai un site Web: www.example.com - et je souhaite que mes clients ne puissent accéder au site qu'à partir d'un seul onglet dans un navigateur. S'ils ouvrent un autre onglet et chargent le site (ou une sous-page du site), je souhaite une alerte " Impossible d'ouvrir plusieurs instances ", puis les redirige vers une page d'erreur.

Une chose à noter - si l'utilisateur change l'adresse de www.example.com/action/door/mine.aspx à www.example.com - cela devrait fonctionner correctement, car l’utilisateur se trouve dans le même onglet (original).

Toute aide serait appréciée. Merci d'avance.

14
Jason

EDIT2:

C'est exactement ce qui est mentionné dans cette réponse , vous avez besoin de 2 identifiants:

  1. Un aléatoire
  2. Un seul cohérent (ce sera en fait notre SSID, puisque vous limitez les onglets d’un seul navigateur, il est préférable d’obtenir les paramètres uniques du navigateur de formulaires générés).

Vous pouvez en générer une cohérente à partir de l'agent utilisateur du navigateur ou à partir du serveur. stocker les deux côté serveur.
Stockez la valeur aléatoire dans la propriété window.name qui est spécifique à un onglet.
Envoyez à votre serveur une pulsation toutes les 1 à 2 secondes contenant à la fois un identifiant cohérent et un aléatoire. si le serveur ne parvient pas à recevoir le signal de présence, il nettoie la base de données et annule l’enregistrement des clients morts.
à la demande de chaque navigateur, vérifiez la valeur avec window.name. s'il était manquant, vérifiez auprès du serveur si le précédent onglet est fermé ou non (nettoyé de la base de données).

Si oui, générez une nouvelle paire pour le client si non, rejetez-le.


Deux suggestions qui me viennent à l’esprit:

  1. côté serveur (meilleur):fournissez tous vos clients, un nom d'utilisateur et un mot de passe. demandez-leur lors de leur première visite sur votre site d'entrer avec leurs informations d'identification. puis sur toutes les autres demandes, vérifiez si l'utilisateur avec lesdites informations d'identification est déjà connecté ou non.
 Client * 
 | 
 | 
 Serveur ---> Vérifiez si 
 Est déjà connecté 
 Ou non? 
 ______________ 
 | | 
 oui non 
 | | 
 permet de le rejeter 
 lui 
  1. Client-side: _ Si vous avez vraiment besoin d'une vérification approfondie, utilisez evercookie pour enregistrer un cookie already-logged-in sur la machine du client.

Note latérale} _ Sachez que chaque tentative côté client n'est pas sécurisée du tout! côté client devrait aider côté serveur, il ne devrait pas être utilisé comme la seule et unique source de sécurité. même tous les biscuits peuvent être supprimés alors, donnez ma première suggestion un essai.


EDIT:

Evercookie fait vraiment du bon travail pour stocker les cookies de zombies les plus sécurisés de tous les temps, mais comme la bibliothèque elle-même est un peu lourde pour les navigateurs (stocker un cookie prend plus de 100 ms à chaque fois), il n'est pas vraiment recommandé de l'utiliser dans des applications Web réelles.

utilisez-les à la place si vous utilisez une solution côté serveur:

7
Sepehr

J'ai créé une solution simple pour cela. La mise en page maître crée un onglet GUID et le stocke dans la zone sessionStorage de l'onglet. Utilisation d’un écouteur d’événements sur la zone de stockage J’écris l’onglet GUID dans la zone de stockage local des sites. L'écouteur compare ensuite les onglets GUID à ceux écrits dans la mémoire de stockage du site. S'ils diffèrent, il sait que plusieurs onglets sont ouverts. 

Donc, si j'ai trois onglets A, B, C, puis cliquez sur quelque chose dans l'onglet C, les onglets A et B détectent un autre onglet est ouvert et avertir l'utilisateur de cela. Je n'ai pas encore résolu le problème, donc le dernier onglet utilisé est celui de la notification, travail en cours.

Voici le JS que j'ai dans la page maître, plus dans la page de connexion, j'ai un localStorage.Clear pour effacer le dernier onglet de la session précédente.

    // multi tab detection
function register_tab_GUID() {
    // detect local storage available
    if (typeof (Storage) !== "undefined") {
        // get (set if not) tab GUID and store in tab session
        if (sessionStorage["tabGUID"] == null) sessionStorage["tabGUID"] = tab_GUID();
        var guid = sessionStorage["tabGUID"];

        // add eventlistener to local storage
        window.addEventListener("storage", storage_Handler, false);

        // set tab GUID in local storage
        localStorage["tabGUID"] = guid;
    }
}

function storage_Handler(e) {
    // if tabGUID does not match then more than one tab and GUID
    if (e.key == 'tabGUID') {
        if (e.oldValue != e.newValue) tab_Warning();
    }
}

function tab_GUID() {
    function s4() {
        return Math.floor((1 + Math.random()) * 0x10000)
          .toString(16)
          .substring(1);
    }
    return s4() + s4() + '-' + s4() + '-' + s4() + '-' +
      s4() + '-' + s4() + s4() + s4();
}

function tab_Warning() {
    alert("Another tab is open!");
}

Note: c'est IE9 +

J'espère que cela t'aides.

18
user755404

Je sais que ce message est assez ancien, mais au cas où cela aiderait quelqu'un, j’ai récemment envisagé de faire la même chose en utilisant localStorage et sessionStorage.

Similaire La réponse d'Anthony , elle définit un intervalle pour s'assurer que l'onglet d'origine garde l'entrée fraîche, de sorte que si le navigateur se bloque ou se ferme d'une manière ou d'une autre sans appeler l'événement unload (inclus dans les commentaires mais ne faisant pas partie du code à tester) l’application s’exécuterait correctement dans une nouvelle fenêtre du navigateur.

Évidemment, vous changeriez "l'onglet est bon", "l'onglet est mauvais" les conditions pour faire la logique que vous voulez.

Oh, et aussi, la méthode createGUID est juste un utilitaire pour rendre l'identifiant de session unique ... c'est de cette réponse à une question précédente (je voulais m'assurer que je n'en prenais pas le crédit).

https://jsfiddle.net/yex8k2ts/30/

let localStorageTimeout = 15 * 1000; // 15,000 milliseconds = 15 seconds.
let localStorageResetInterval = 10 * 1000; // 10,000 milliseconds = 10 seconds.
let localStorageTabKey = 'test-application-browser-tab';
let sessionStorageGuidKey = 'browser-tab-guid';

function createGUID() {
  let guid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
    /*eslint-disable*/
    let r = Math.random() * 16 | 0,
      v = c == 'x' ? r : (r & 0x3 | 0x8);
    /*eslint-enable*/
    return v.toString(16);
  });

  return guid;
}

/**
 * Compare our tab identifier associated with this session (particular tab)
 * with that of one that is in localStorage (the active one for this browser).
 * This browser tab is good if any of the following are true:
 * 1.  There is no localStorage Guid yet (first browser tab).
 * 2.  The localStorage Guid matches the session Guid.  Same tab, refreshed.
 * 3.  The localStorage timeout period has ended.
 *
 * If our current session is the correct active one, an interval will continue
 * to re-insert the localStorage value with an updated timestamp.
 *
 * Another thing, that should be done (so you can open a tab within 15 seconds of closing it) would be to do the following (or hook onto an existing onunload method):
 *      window.onunload = () => { 
                localStorage.removeItem(localStorageTabKey);
      };
 */
function testTab() {
  let sessionGuid = sessionStorage.getItem(sessionStorageGuidKey) || createGUID();
  let tabObj = JSON.parse(localStorage.getItem(localStorageTabKey)) || null;

    sessionStorage.setItem(sessionStorageGuidKey, sessionGuid);

  // If no or stale tab object, our session is the winner.  If the guid matches, ours is still the winner
  if (tabObj === null || (tabObj.timestamp < new Date().getTime() - localStorageTimeout) || tabObj.guid === sessionGuid) {
    function setTabObj() {
      let newTabObj = {
        guid: sessionGuid,
        timestamp: new Date().getTime()
      };
      localStorage.setItem(localStorageTabKey, JSON.stringify(newTabObj));
    }
    setTabObj();
    setInterval(setTabObj, localStorageResetInterval);
    return true;
  } else {
    // An active tab is already open that does not match our session guid.
    return false;
  }
}

if (testTab()) {
  document.getElementById('result').innerHTML = 'tab is good';
} else {
  document.getElementById('result').innerHTML = 'tab is bad';
}
5
timkellypa
1
kiranvj

Pourquoi voulez-vous faire cela?

Peut essayer de faire du piratage laid, mais le résultat serait: Il y a no de manière à supprimer complètement ce comportement.

Cela ne pourrait pas être résolu par JavaScript, car il est toujours possible que l'utilisateur ait désactivé JavaScript dans son navigateur ou n'autorise qu'un certain sous-ensemble.

L'utilisateur peut ouvrir un nouveau navigateur, utiliser un ordinateur différent, etc. pour visiter plusieurs pages à la fois.

Mais plus important:

En outre, votre site serait le seul site à avoir ce comportement et, pour cette raison, cela dérouterait tous ceux qui utilisent votre site, car il ne fonctionne pas comme un site Web devrait fonctionner. Tous ceux qui essaient d'ouvrir un deuxième onglet penseront: "C'est étrange. Ce site Web est nul parce qu'il est différent des sites Web. Je ne reviendrai pas!" ;-)

1
TheHippo

Le meilleur moyen de résoudre ce problème consiste à avoir des identifiants de session uniques.

Par exemple, chaque page contient un identifiant de session valide pour une visite, unique et aléatoire. Lorsque vous cliquez sur un lien, il utilisera & invalidera l'identifiant de session et la nouvelle page aura un nouveau ID de session.

Cela obligera l'utilisateur à toujours parcourir la fenêtre ou l'onglet le plus récent, et empêchera également le vol de session par le fil. Toute tentative de réutilisation d'un ancien ID de session devrait immédiatement tuer également les ID de session actifs de cet utilisateur.

Il est également important de stocker dans le système de gestion de session les pages accessibles à partir de la page X. Si la page X (avec l'ID de session abc) contient des liens vers les pages 1, 2 et 3, toute tentative de consultation de la page 4 avec l'ID de session abc , va échouer et tuer également la session.

Cela obligera l'utilisateur à toujours disposer d'une piste de session unique et à toujours suivre la logique du site. Toute tentative d'avancer, de revenir en arrière, d'utiliser l'historique ou les entrées de journal, ou d'ouvrir plusieurs fenêtres ou onglets, échouera et déconnectera l'utilisateur de toutes les fenêtres, onglets et périphériques.

Tout cela peut être complètement implémenté côté serveur, sans aucune logique côté client.

0
sebastian nielsen

J'ai écrit ceci pour empêcher l'accès à une page du centre d'appels dans plusieurs onglets. Cela fonctionne bien et est purement client. Il suffit de mettre à jour la partie else if pour faire ce que vous voulez si elle détecte un nouvel onglet.

// helper function to set cookies
function setCookie(cname, cvalue, seconds) {
    var d = new Date();
    d.setTime(d.getTime() + (seconds * 1000));
    var expires = "expires="+ d.toUTCString();
    document.cookie = cname + "=" + cvalue + ";" + expires + ";path=/";
}

// helper function to get a cookie
function getCookie(cname) {
    var name = cname + "=";
    var decodedCookie = decodeURIComponent(document.cookie);
    var ca = decodedCookie.split(';');
    for(var i = 0; i < ca.length; i++) {
        var c = ca[i];
        while (c.charAt(0) == ' ') {
            c = c.substring(1);
        }
        if (c.indexOf(name) == 0) {
            return c.substring(name.length, c.length);
        }
    }
    return "";
}

// Do not allow multiple call center tabs
if (~window.location.hash.indexOf('#admin/callcenter')) {
    $(window).on('beforeunload onbeforeunload', function(){
        document.cookie = 'ic_window_id=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;';
    });

    function validateCallCenterTab() {
        var win_id_cookie_duration = 10; // in seconds

        if (!window.name) {
            window.name = Math.random().toString();
        }

        if (!getCookie('ic_window_id') || window.name === getCookie('ic_window_id')) {
            // This means they are using just one tab. Set/clobber the cookie to prolong the tab's validity.
            setCookie('ic_window_id', window.name, win_id_cookie_duration);
        } else if (getCookie('ic_window_id') !== window.name) {
            // this means another browser tab is open, alert them to close the tabs until there is only one remaining
            var message = 'You cannot have this website open in multiple tabs. ' +
                'Please close them until there is only one remaining. Thanks!';
            $('html').html(message);
            clearInterval(callCenterInterval);
            throw 'Multiple call center tabs error. Program terminating.';
        }
    }

    callCenterInterval = setInterval(validateCallCenterTab, 3000);
}
0
Anthony Vipond