2012-06-13 8 views
12

Chcę, aby użytkownicy przeglądali moją witrynę od tylko jedną kartę w przeglądarce. Jak to zrobić? Czy używałbym javascript i ciasteczek?Zatrzymaj osoby, które mają moją stronę internetową załadowaną na wiele kart.

Na przykład, mam stronę internetową: www.example.com - i chcę, aby moi klienci tylko być w stanie odwiedzić witrynę z jednej karcie w jednej przeglądarce. Jeśli otworzą kolejną kartę i załadują witrynę (lub podstronę strony) - chcę wygenerować alert "Nie można otworzyć wielu instancji", a następnie przekierować je na stronę błędu.

Raz rzeczą, aby pamiętać - jeśli użytkownik zmieni adres z www.example.com/action/door/mine.aspx do www.example.com - że powinny działać prawidłowo, ponieważ posiadał użytkownik znajduje się na tej samej (oryginalnej) karcie.

Każda pomoc zostanie doceniona. Z góry dziękuję.

+0

Cóż można mieć trochę javascript odpowiedzieć na wniosek pulsu co jakiś czas z identyfikatorem sesji bieżącego użytkownika, zanim strona ładuje sprawdzić, czy ten ajax bicie serca przychodzi w dla tej sesji, jeśli tak jest, zamykasz ją. – gideon

+2

Możesz to zrobić w ograniczonym zestawie przypadków, ale generalnie jest to niemożliwe.Użytkownicy mogą wyłączyć niektóre lub wszystkie skrypty, odmówić dostępu do plików cookie i historii, a niektóre przeglądarki pozwalają na uruchomienie zupełnie nowej sesji w zakładce, więc inne niż wykrywanie IP (które serwer może zrobić łatwo) nie masz szczęścia. – RobG

Odpowiedz

7

EDIT2:

Jest to dokładny rzeczą, która jest wymieniona w this answer, musisz 2 identyfikatory :

  1. Jeden losowy jeden
  2. Jeden spójny jeden (to będzie nasz SSID faktycznie, ponieważ ograniczają zaczepy jednej przeglądarki, to lepiej, aby uzyskać unikalne parametry generowane przeglądarki forma za)

Można wygenerować spójnego jedną z aplikacji klienckiej przeglądarki lub uzyskać to od strony serwera. zapisz oba z nich po stronie serwera.
Zapisz losową właściwość w postaci window.name, która jest specyficzna dla karty.
Wysyłaj pulsu co 1 ~ 2 sekundy do serwera zawierającego zarówno spójny identyfikator, jak i losowy. jeśli serwer nie otrzyma pulsu, wyczyści bazę danych i wyrejestruje martwych klientów.
na żądanie każdej przeglądarki sprawdź wartość window.name. jeśli go brak, sprawdź po stronie serwera, czy poprzednia karta jest zamknięta, czy nie (wyczyszczone z bazy danych).

Jeśli tak, wygeneruj nową parę dla klienta, jeśli nie, odrzuć go.


Dwa sugestie na szczycie mojej głowie:

  1. Server-side (lepiej): zapewnić wszystkim swoim klientom, nazwę użytkownika i hasło. poprosić ich podczas pierwszej wizyty w witrynie, aby wprowadzić swoje dane uwierzytelniające. następnie na każde inne żądanie sprawdź, czy użytkownik z tymi referencjami jest już zalogowany, czy nie.
 
    Client * 
     | 
     | 
     Server ---> Check whether 
        Already logged 
        or not? 
        ______________ 
        |   | 
        yes   no 
        |   | 
       permit  reject 
        him  him 
  1. Client-side: Jeśli naprawdę potrzebują silnej sprawdzenie tego, użyj evercookie do przechowywania already-logged-in cookie na komputerze klienta.

Side-note: wiem, że każda próba na stronie klienta nie jest bezpieczna w ogóle! po stronie klienta powinien pomóc po stronie serwera, nie powinien być używany jako jedyne źródło bezpieczeństwa. even evercookies can be deleted więc, dajcie moją pierwszą sugestię.


EDIT:

Evercookie jest naprawdę robi dobrą robotę na przechowywanie najbardziej bezpiecznych ciasteczek zombie kiedykolwiek ale ponieważ sama biblioteka jest trochę ciężki dla przeglądarek (przechowywania cookie trwa dłużej niż 100ms za każdym razem) to naprawdę nie jest zalecane do używania w rzeczywistej aplikacji internetowej.

korzystać z tych zamiast jeśli poszedł z roztworu po stronie serwera:

+0

dzięki za odpowiedź. kilka pytań . 1: po stronie serwera, używam sesji do przechowywania poświadczeń użytkownika i mam stronę login.aspx do zalogowania się użytkownika, ponieważ karty w jednej sesji współużytkowania przeglądarki, więc jeśli zaloguj się do systemu w jednej karcie (tabA) i skopiuj cały URL (np. www.xx.com/some/test.aspx) i otworzyć go z innej zakładki (tabB), przeglądarka rozważy zalogowanie użytkownika, aw zakładce B nie przekieruje do strony login.aspx. teraz sprawdzam, czy evercookie może rozwiązać ten problem, mam nadzieję, że to zrobi, dzięki – Jason

+0

@Jason Nie! jeśli korzystasz z rozwiązania po stronie serwera, nie używaj evercookie, użyj tego zamiast: [Sposób obejścia sesji ASP.NET jest współdzielony w wielu oknach kart] (http://stackoverflow.com/a/2839183/1055628). evercookie jest bardzo bezpiecznym rozwiązaniem, takim jak bankowość i takie (patrz moja zredagowana odpowiedź). – Sepehr

+0

dziękuję, rozwiązanie "na okrągło ...", czy to sprawia, że ​​każda strona z zakładką jest jedną sesją? nie rozwiązuje bezpośrednio mojego problemu, ale w inny sposób? – Jason

1

Dlaczego chcesz to zrobić?

Można spróbować zrobić brzydkie hakowanie, ale wynik byłby: Istniejesposób, w jaki można całkowicie powstrzymać to zachowanie.

Nie można tego rozwiązać za pomocą JavaScript, ponieważ zawsze istnieje możliwość, że użytkownik wyłączył JavaScript w swojej przeglądarce lub zezwala tylko na określony podzbiór.

Użytkownik może otworzyć nową przeglądarkę, użyć innego komputera itp., Aby odwiedzić wiele stron jednocześnie.

Ale ważniejsze:

Ponadto, witryna będzie jedynym miejscem, które ma ten problem i dlatego będzie to zmylić każdego, który wykorzystuje swoją stronę, bo to nie działa jak strony internetowej powinno działać. Każdy, kto spróbuje otworzyć drugą kartę, pomyśli: "To dziwne, ta strona jest do bani, ponieważ powinna być inna niż strony internetowe, nie przyjadę ponownie". ;-)

+2

Dzięki Hippo, ponieważ nasza aplikacja ma kilka funkcji, takich jak system logowania do rejestrowania działań po stronie klienta, a przy użyciu wielu zakładek, system będzie się mylić i nie wiem, co dokładnie się stało, tor będzie trudny, jeśli js niepełnosprawnych , cóż, nie możemy nic z tym zrobić, jeśli tak, nie może on korzystać z naszego systemu. to dziwne, ale niestety musimy to zrobić. – Jason

+0

Zwykle, jeśli pewna sesja jest zarządzana na serwerze i łatwiej jest wyłączyć wiele kart, niż pozwolić użytkownikowi na jednoczesne uruchomienie dwóch kreatorów. – otonglet

15

Utworzyłem proste rozwiązanie dla tego. Układ strony głównej tworzy identyfikator GUID karty i przechowuje go w obszarze sessionStorage karty. Używając detektora zdarzeń w obszarze pamięci zapisuję zakładkę GUID do obszaru localStorage. Następnie słuchacz porównuje zakładki GUID z zapisanym w pamięci witryny, a jeśli się różnią, wie, że jest otwarta więcej niż jedna zakładka.

Więc jeśli mam trzy zakładki A, B, C, kliknij coś w zakładce C, zakładka A i B wykryją, że inna zakładka jest otwarta i ostrzegam użytkownika o tym. Jeszcze nie naprawiłem, więc ostatnia zakładka otrzymała powiadomienie, praca w toku.

Oto JS, który mam na stronie wzorcowej, plus na stronie logowania Mam localStorage.Clear, aby usunąć ostatnią zakładkę z poprzedniej sesji.

// 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!"); 
} 

Uwaga: To IE9 +

Nadzieja to pomaga.

+0

dziękuję. verry fajne rozwiązanie –

+0

To nie działa dla mnie. Czy muszę wywołać dowolną funkcję js podczas ładowania? –

+0

@DanielASathishKumar wszystkie JS powinny znajdować się na stronie i należy uruchomić register_tab_GUID() podczas ładowania strony. – Darren

0

Najlepszym sposobem rozwiązania tego problemu jest posiadanie identyfikatorów jednorazowych sesji.

Np. Każda strona zawiera identyfikator sesji, który jest ważny dla jednej wizyty, jest unikalny i losowy. Po kliknięciu dowolnego linku zostanie użyta & unieważnienie identyfikatora sesji, a nowa strona będzie miała nowy identyfikator sesji.

Spowoduje to, że użytkownik będzie zawsze przeglądał najnowsze okno lub kartę, a także zapobiega przechwytywaniu sesji przez przewód. Każda próba ponownego użycia starego identyfikatora sesji powinna natychmiastowo zabić również aktywne identyfikatory sesji tego użytkownika.

Ważne jest również, aby zapisać w systemie zarządzania sesjami, które strony są dostępne ze strony X. Jeśli więc strona X (z identyfikatorem sesji abc) zawiera łącza do strony 1, 2 i 3, każda próba odwiedzenia strony 4 z identyfikatorem sesji abc, zawiedzie, a także zabije sesję.

To zmusi użytkownika, aby zawsze miał jedną ścieżkę sesji i zawsze postępuje zgodnie z logiką na stronie. Każda próba przejścia do przodu, powrotu, użycia historii lub dziennika, lub otwarcia wielu okien lub kart, zakończy się niepowodzeniem i wylogowaniem użytkownika we wszystkich oknach, kartach i urządzeniach.

Wszystko to może zostać całkowicie zaimplementowane po stronie serwera, bez jakiejkolwiek logiki po stronie klienta.

0

Napisałem to, aby zatrzymać dostęp do strony centrum obsługi w wielu zakładkach. Działa dobrze i jest czysto po stronie klienta. Po prostu zaktualizuj część else if, aby zrobić, co chcesz, jeśli wykryje nową kartę.

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

Znam ten post jest dość stary, ale w przypadku pomaga nikomu, ja niedawno wyglądał w zasadzie robi to samo, używając localStorage i sessionStorage.

Podobny , ustawia interwał, aby upewnić się, że początkowa zakładka utrzymuje świeżą pozycję, tak, że jeśli przeglądarka zawiesi się lub jakoś zamknie bez wywoływania zdarzenia zwolnienia (zawarte w komentarzach, ale nie część kodu do celów testowych), a następnie pojawi się krótkie opóźnienie, zanim aplikacja będzie działać poprawnie w nowym oknie przeglądarki.

Oczywiście zmienisz "zakładka jest dobra", "karta jest zła" warunki do zrobienia jakiejkolwiek logiki chcesz.

Aha, a także metoda createGUID jest tylko narzędziem, które czyni unikalny identyfikator sesji ... to od this answer do poprzedniego pytania (chciałem się upewnić, że nie biorę za to uznania).

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'; 
} 
Powiązane problemy