web-dev-qa-db-ja.com

自分のウェブサイトが複数のタブに読み込まれるのを防ぐ

ユーザーがブラウザの1つのタブからのみ自分のサイトを閲覧できるようにしたい。これはどのように行うことができますか? JavaScriptとCookieを使用しますか?

たとえば、私はウェブサイトを持っています:www.example.com-そして、クライアントにのみアクセスできるようにしたい1つのブラウザの1つのタブからのサイト。彼らが別のタブを開いてサイト(またはサイトのサブページ)をロードした場合-「Cannot open multiple instances」というアラートが必要です。次に、それらをエラーページにリダイレクトします。

注意が必要なこと-ユーザーがwww.example.com/action/door/mine.aspxからアドレスを変更した場合to www.example.com-ユーザーは同じ(元の)タブにいるため、問題なく動作します。

どんな助けでもありがたいです。前もって感謝します。

22
Jason

このための簡単なソリューションを作成しました。マスターページレイアウトは、タブを作成しますGUID=それをタブのsessionStorage領域に格納します。ストレージ領域でイベントリスナーを使用して、タブを記述しますGUID次に、リスナーはサイトのlocalStorage領域にアクセスします。リスナーは、タブGUIDをサイトのストレージに書き込まれたタブと比較し、それらが異なる場合、複数のタブが開いていることを認識します。

したがって、A、B、Cの3つのタブがある場合、タブCで何かをクリックすると、タブAとBが別のタブが開いていることを検出し、これをユーザーに警告します。私はまだそれを修正する必要がありませんので、最後に使用されたタブは通知の通知、進行中の作業です。

ここに私がマスターページに持っているJSがあります、そしてログインページに私が前のセッションから最後のタブをクリアするためにlocalStorage.Clearを持っています。

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

注:IE9 +です。

お役に立てれば。

23
user755404

私はこの投稿がかなり古いことを知っていますが、誰かを助けるために、最近localStorageとsessionStorageを使用して基本的に同じことをすることを検討しました。

同様に Anthonyの答え は、元のタブがエントリを最新の状態に保つように間隔を設定します。これにより、ブラウザがクラッシュしたり、アンロードイベントを呼び出さずに何らかの理由で閉じたりした場合(コメントには含まれますが、テスト目的のコード)の場合、アプリケーションが新しいブラウザウィンドウで適切に実行されるまでに少し時間がかかります。

明らかに、「タブは良好」、「タブは不良」の条件を変更して、必要なロジックを実行します。

ああ、また、createGUIDメソッドは、セッション識別子を一意にするためのユーティリティにすぎません... この答え から前の質問へ(私がそれを信用していないことを確認したかったからです) )。

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

これを解決する最良の方法は、1回限りのセッションIDを持つことです。

たとえば、各ページにはセッションIDが含まれています。これは1回の訪問に対して有効であり、一意でランダムです。いずれかのリンクをクリックすると、セッションIDが使用および無効化され、新しいページに新しいセッションIDが設定されます。

これにより、ユーザーは常に最新のウィンドウまたはタブを参照する必要があり、また、ネットワークを介したセッションの盗用も防止されます。古いセッションIDを再利用しようとすると、そのユーザーのアクティブなセッションIDもすぐに強制終了されます。

セッション管理システムに、ページXからアクセス可能なページを保存することも重要です。したがって、ページX(セッションID abcを持つ)にページ1、2および3へのリンクが含まれている場合、セッションID abcを持つページ4にアクセスしようとする試み、失敗し、セッションも強制終了します。

これにより、ユーザーは常に1つのセッショントラックを持ち、常にサイトのロジックに従う必要があります。履歴やログ全体を使用したり、複数のウィンドウやタブを開いたり、前に戻ったり、すべてのウィンドウ、タブ、デバイスでユーザーをログアウトしようとすると失敗します。

これはすべて、クライアント側のロジックなしでサーバー側に完全に実装できます。

0

これは、コールセンターページが複数のタブでアクセスされないようにするために作成しました。それはうまく機能し、純粋にクライアント側です。 else ifパーツを更新して、新しいタブが検出された場合に必要な処理を行うだけです。

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

なぜこれを実行しますか?

醜いハッキングを試みることもできますが、結果は次のようになります。noこの方法を完全に抑制する方法があります。

これは、ユーザーがブラウザでJavaScriptを無効にしている可能性があるか、特定のサブセットのみが許可されている可能性があるため、JavaScriptでは解決できませんでした。

ユーザーは、新しいブラウザを開いたり、別のコンピュータを使用したりして、一度に複数のページにアクセスできます。

しかしより重要:

また、あなたのサイトはこの振る舞いを持つ唯一のサイトであり、このため、これはあなたのサイトを使用するすべての人を混乱させます。 2番目のタブを開こうとする人は誰でも、「これは奇妙です。このWebサイトは他のWebサイトとは異なるため、つまらないです。私は二度と来ません!」と思います。 ;-)

0
TheHippo