35

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

たとえば、私はWebサイトwww.example.comを持っていますが、クライアントが1つのブラウザーの1つのタブからのみサイトにアクセスできるようにしたいと考えています。別のタブを開いてサイト(またはサイトのサブページ)をロードした場合-「複数のインスタンスを開くことができません」というアラートが必要です。その後、エラーページにリダイレクトします。

注意すべき点が1つあります。ユーザーがアドレスをwww.example.com/action/door/mine.aspxからwww.example.comに変更した場合、ユーザーは同じ(元の)タブにいるため、正常に機能するはずです。

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

4

10 に答える 10

29

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

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

これがマスターページにある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+です

お役に立てれば。

于 2015-09-24T11:40:44.287 に答える
16

更新-2020

クライアント側の実装:

両方のコンテキストが同じオリジンからのものである場合、ブラウジングコンテキスト(ウィンドウ、タブ、フレーム、またはiframe)間での通信を可能にするBroadcastChannelAPIを利用できます。

1番目のタブからWebサイトをロードしている2番目のタブを検出するための簡単な実装:

    //in entry point of your app (index.js)    

    const channel = new BroadcastChannel('tab');

    channel.postMessage('another-tab');
    // note that listener is added after posting the message

    channel.addEventListener('message', (msg) => {
      if (msg.data === 'another-tab') {
        // message received from 2nd tab
        alert('Cannot open multiple instances');
      }
    });

localStorageこれはまたはを使用せずcookies、1番目のタブがオフラインで2番目のタブがロードされている場合でも機能します。

注:これはSafariとIE11ではまだサポートされていません:(

ブラウザの互換性に注意してください。

ただし、その仕事をするポリフィルが利用可能です。

于 2020-06-06T12:18:14.610 に答える
12

EDIT2:

それはこの答えで言及されている正確なものです、あなたは2つのIDが必要です:

  1. 1つのランダムなもの
  2. 1つの一貫したもの(これは実際にはSSIDになります。単一のブラウザーのタブを制限するため、ブラウザーの固有のパラメーターから生成されたものを取得することをお勧めします)

ブラウザのユーザーエージェントから一貫性のあるものを生成することも、サーバー側から取得することもできます。両方をサーバー側に保存します。タブ固有のプロパティに
ランダムなものを保存します。 一貫性のあるIDとランダムなIDの両方を含むハートビートを1〜2秒ごとにサーバーに送信します。サーバーがハートビートの受信に失敗した場合、サーバーはデータベースをクリーンアップし、停止したクライアントの登録を解除します。 すべてのブラウザの要求で、値を確認してください。欠落している場合は、前のタブが閉じているかどうか(データベースからクリーンアップされているかどうか)をサーバー側に確認してください。window.name

window.name

はいの場合、クライアントの新しいペアを生成し、いいえの場合、それらを拒否します。


私の頭の上の2つの提案:
  1. サーバー側(より良い):すべてのクライアント、ユーザー名、およびパスワードを提供します。あなたのサイトへの最初の訪問時に彼らに彼らの資格情報を入力するように要求してください。次に、他のすべての要求で、上記の資格情報を持つユーザーがすでにログインしているかどうかを確認します。
  クライアント *
         |
         |
      サーバー--->かどうかを確認します
                  すでにログに記録されています
                     か否か?
                  ______________
                   | |
                  はい・いいえ
                   | |
                 拒否を許可する
                  それらそれら
  1. クライアント側:これを強力にチェックする必要がある場合は、 evercookiealready-logged-inを使用してクライアントのマシンにCookieを保存

補足:クライアント側でのすべての試みが安全ではないことを知っておいてください!クライアント側はサーバー側を支援する必要があり、唯一のセキュリティソースとして使用するべきではありません。エバークッキーでさえ削除できるので、私の最初の提案を試してみてください。


**編集:**

Evercookieは、これまでで最も安全なゾンビCookieを保存するのに非常に優れていますが、ライブラリ自体はブラウザにとって少し重いため(Cookieの保存には毎回100ミリ秒以上かかります)、実際のWebアプリでの使用はお勧めしません。

サーバー側のソリューションを使用した場合は、代わりにこれらを使用してください。

于 2012-06-13T05:00:05.750 に答える
6

この投稿はかなり古いことは知っていますが、誰かに役立つ場合に備えて、最近、localStorageとsessionStorageを使用して基本的に同じことを行うことを検討しました。

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

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

また、createGUIDメソッドは、セッションIDを一意にするための単なるユーティリティです...これは、この回答から前の質問への回答です(私がそのことを認めていないことを確認したかったのです)。

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';
}
于 2017-08-16T15:35:32.470 に答える
1

同じ問題(および解決策):https ://sites.google.com/site/sarittechworld/track-client-windows

同様: http: //www.codeproject.com/Articles/35859/Detect-and-prevent-multiple-windows-or-tab-usage-i

于 2012-06-13T04:45:07.713 に答える
1

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

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

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

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

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

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

于 2015-11-25T22:42:31.977 に答える
0

なぜあなたはこれをしたいのですか?

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

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

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

しかし、もっと重要なのは:

また、あなたのサイトはこの振る舞いをする唯一のサイトであり、この理由であなたのサイトを使用するすべての人を混乱させます。なぜなら、それはウェブサイトが機能するはずのように機能しないからです。2番目のタブを開こうとする人は誰でも、「これは奇妙です。このWebサイトは、Webサイトとは異なるため、ひどいです。二度と来ません!」と思うでしょう。;-)

于 2012-06-13T04:32:03.873 に答える
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);
}
于 2017-04-29T03:58:42.997 に答える
0
window.addEventListener('load', function () {
    if (localStorage.getItem('web_browser') == null) {
        // new tab
        localStorage.setItem('web_browser', 'true');
        window.addEventListener('unload', function() {
            localStorage.removeItem('web_browser');
        })
    } else {
        // duplicate tab
        return;
    }
})

このスクリプトは、ユーザーが現在のページまたはタブを複製しないようにするhtmlページの先頭に配置します。

于 2022-01-23T11:22:31.597 に答える
0

代わりに新しいタブでアラートが必要な場合を処理するためにrehman_00001の回答を拡張します。

const channel = new BroadcastChannel('tab');
let isOriginal = true;

channel.postMessage('another-tab');
// note that listener is added after posting the message

channel.addEventListener('message', (msg) => {
    if (msg.data === 'another-tab' && isOriginal) {
        // message received from 2nd tab
        // reply to all new tabs that the website is already open
        channel.postMessage('already-open');
    }
    if (msg.data === 'already-open') {
        isOriginal = false;
        // message received from original tab
        // replace this with whatever logic you need
        alert('Cannot open multiple instances');
    }
});

于 2022-02-10T17:24:26.563 に答える