Javascript 阻止人们在多个选项卡上加载我的网站

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/11008177/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-24 04:00:31  来源:igfitidea点击:

Stop people having my website loaded on multiple tabs

javascriptcookiesbrowsertabs

提问by Jason

I want users to browse my site from onlyone tab in their browser. How can this be done? Would I use javascript and cookies?

我希望用户仅从浏览器中的一个选项卡浏览我的网站。如何才能做到这一点?我会使用 javascript 和 cookie 吗?

For example, I have a website: www.example.com- and I want my clients to only be able to visit the site from one single tab in one browser. If they open another tab and load the site (or a subpage of the site) - I want an alert "Can't open multiple instances", and then redirect them to an error page.

例如,我有一个网站:www.example.com- 我希望我的客户只能从一个浏览器中的一个选项卡访问该网站。如果他们打开另一个选项卡并加载站点(或站点的​​子页面) - 我想要一个警报“无法打开多个实例”,然后将它们重定向到错误页面。

Once thing to note - if the user changes the address from www.example.com/action/door/mine.aspxto www.example.com- that should work fine, because the user is in the same (original) tab.

有一点需要注意 - 如果用户将地址从www.example.com/action/door/mine.aspx更改为www.example.com- 应该可以正常工作,因为用户位于同一个(原始)选项卡中。

Any help will be appreciated. Thanks in advance.

任何帮助将不胜感激。提前致谢。

采纳答案by Sepehr

EDIT2:

编辑2:

It's the exact thing which is mentioned at this answer, You need 2 IDs:

这是这个答案中提到的确切内容,您需要2个ID:

  1. One random one
  2. One consistent one (this will be our SSID actually, since you limit tabs of a single browser, it's better to get generated form browser's unique parameters)
  1. 随机一张
  2. 一个一致的(这实际上是我们的SSID,因为你限制了单个浏览器的标签,最好得到生成的表单浏览器的唯一参数)

You can generate consistent one from browser's user-agent or get it from server-side. store both of them server-side.
Store the random one in window.nameproperty which is tab-specific.
Send a heartbeat every 1~2 seconds to your server containing both consistent ID and random one. if server fails to receive the heartbeat, it cleans up database and de-register dead clients.
on every browser's request, check window.namefor the value. if it were missing, check with the server-side whether if the previous tab is closed or not (cleaned from database).

您可以从浏览器的用户代理生成一致的,也可以从服务器端获取。将它们都存储在服务器端。
将随机一个存储在window.name特定于选项卡的属性中。
每 1~2 秒向您的服务器发送一次心跳,其中包含一致 ID 和随机 ID。如果服务器未能接收到心跳,它会清理数据库并取消注册死客户端。
在每个浏览器的请求中,检查window.name值。如果丢失,请与服务器端检查上一个选项卡是否已关闭(从数据库中清除)。

If yes, generate a new pair for client if no, reject him.

如果是,则为客户端生成一个新对,如果不是,则拒绝他。



我想到了两个建议:

  1. Server-side (better):provide all your clients, a user name and password. request them on their first visit of your site to enter with their credentials. then on every other request, check for whether user with said credentials is already logged in or not.
  1. 服务器端(更好):提供所有客户端、用户名和密码。要求他们在第一次访问您的网站时输入他们的凭据。然后在每个其他请求中,检查具有所述凭据的用户是否已经登录。
  Client *
         |
         |
      Server ---> Check whether
                  Already logged
                     or not?
                  ______________
                   |          |
                  yes         no
                   |          |
                 permit     reject
                  him        him
  1. Client-side:If you really need a strong check of this, use evercookieto store an already-logged-incookie on client's machine.
  1. 客户端:如果您确实需要对此进行严格检查,请使用evercookiealready-logged-in在客户端计算机上存储cookie。

Side-note:Do know that every attempt in client side is not secure at all! client-side should help server-side, it shouldn't be used as the one and only source of security. even evercookies can be deletedso, give my first suggestion a go.

旁注:要知道客户端的每一次尝试都根本不安全!客户端应该帮助服务器端,它不应该被用作唯一的安全来源。甚至 evercookies 也可以删除,所以请尝试一下我的第一个建议。



EDIT:编辑:

Evercookie is really doing a good job at storing most secure zombie cookies ever but since the library itself is a little bit heavy for browsers (storing a cookie takes more than 100ms each time) it's not really recommended for using in real-world web app.

Evercookie 在存储最安全的僵尸 cookie 方面确实做得很好,但由于库本身对于浏览器来说有点重(每次存储 cookie 需要超过 100 毫秒),因此不建议在现实世界的 Web 应用程序中使用。

use these instead if you went with server-side solution:

如果您使用服务器端解决方案,请改用这些:

回答by

I've created a simple solution for this. The master page layout creates a tab GUID and stores it in sessionStorage area of the tab. The using an event listener on the storage area I write the tab GUID to the sites localStorage area. The listener then compares the tabs GUID to the one written to site storage and if they differ then it knows more than one tab is open.

我为此创建了一个简单的解决方案。母版页面布局创建一个选项卡 GUID 并将其存储在选项卡的 sessionStorage 区域中。在存储区域上使用事件侦听器我将选项卡 GUID 写入站点 localStorage 区域。然后,侦听器将选项卡 GUID 与写入站点存储的选项卡进行比较,如果它们不同,则它知道打开的选项卡不止一个。

So if I have three tabs A,B,C then click something in tab C, tab A and B detect another tab is open and warn user of this. I haven't yet got to fixing it so the last tab used get's notification, work in progress.

因此,如果我有三个选项卡 A、B、C,则单击选项卡 C、选项卡 A 和 B 中的某些内容会检测到另一个选项卡已打开并警告用户这一点。我还没有修复它,所以最后一个选项卡使用了获取通知,正在进行中。

Here's the JS I have in master page, plus in the login page I have a localStorage.Clearto clear last tab from previous session.

这是我在母版页中的 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!");
}

Note: It's IE9+

注意:这是IE9+

Hope this helps.

希望这可以帮助。

回答by timkellypa

I know this post is pretty old, but in case it helps anybody, I recently looked into basically doing the same thing using localStorage and sessionStorage.

我知道这篇文章已经很老了,但如果它对任何人有帮助,我最近研究了使用 localStorage 和 sessionStorage 基本上做同样的事情。

Similar Anthony's answer, it sets an interval to make sure the originating tab keeps the entry fresh, so that if the browser crashes or somehow closes without calling the unload event (included in the comments but not part of the code for testing purposes), then there would just be a short delay before the application would run properly in a new browser window.

Anthony 的回答类似,它设置了一个时间间隔以确保原始选项卡保持条目新鲜,以便如果浏览器崩溃或以某种方式关闭而不调用卸载事件(包含在注释中,但不是用于测试目的的代码的一部分),然后在应用程序在新的浏览器窗口中正常运行之前,会有短暂的延迟。

Obviously, you would change the "tab is good", "tab is bad" conditions to do whatever logic you want.

显然,您可以更改“制表符好”、“制表符不好”的条件来执行您想要的任何逻辑。

Oh, and also, the createGUID method is just a utility to make the session identifier unique... it is from this answerto a previous question (wanted to make sure I wasn't taking credit for that).

哦,还有,createGUID 方法只是一个使会话标识符唯一的实用程序......它来自这个对上一个问题的回答(想确保我没有因此而受到赞扬)。

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

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';
}

回答by TheHippo

Whydo you want to do this?

你为什么要这样做?

Could try to do some ugly hacking, but the result would be: There is noway you could completely suppress this behaviour.

可以尝试做一些丑陋的黑客攻击,但结果将是:有没有办法可以完全抑制这种行为。

This could not be solved by JavaScript, because there is always the possibility that the user has disabled JavaScript in his browser, or allows only a certain subset.

这无法通过 JavaScript 解决,因为总是有可能用户在其浏览器中禁用了 JavaScript,或者只允许某个子集。

The user could open a new browser, use a different computer, etc. to visit multiple pages at once.

用户可以打开新的浏览器、使用不同的计算机等一次访问多个页面。

But more important:

但更重要的是:

Also, your site would be the only site that has this behaviour and for this reason this will confuse everybody which uses your site, because it doesn't work like a web site should work. Everybody who tries to open a second tab will think: "This is odd. This website sucks because it different then websites should be. I will not come again!" ;-)

此外,您的站点将是唯一具有此行为的站点,因此这会使使用您站点的每个人感到困惑,因为它不像网站应该工作那样工作。每个试图打开第二个标签的人都会想:“这很奇怪。这个网站很糟糕,因为它与网站应该的不同。我不会再来了!” ;-)

回答by sebastian nielsen

The best way to solve this is to have one-time session IDs.

解决此问题的最佳方法是拥有一次性会话 ID。

Eg, each page contain a session ID, that is valid for one visit, is unique, and random. When clicking any one link, it will use & invalidate the session ID, and the new page will have a new session ID.

例如,每个页面都包含一个会话 ID,它对一次访问有效,是唯一的和随机的。当点击任何一个链接时,它将使用 & 使会话 ID 无效,新页面将有一个新的会话 ID。

This will force the user to always browse in the newest window or tab, and also prevents session stealing over the wire. Any attempt to reuse a old session ID should immediately kill also the active session IDs for that user.

这将强制用户始终在最新的窗口或选项卡中浏览,并且还可以防止通过网络窃取会话。任何重用旧会话 ID 的尝试也应立即终止该用户的活动会话 ID。

Its also important to store, in the session management system, which pages is accessible from page X. So if page X (with session ID abc) contains links to page 1, 2 and 3, any attempt to visit page 4 with session ID abc, will fail and also kill the session.

在会话管理系统中存储可以从页面 X 访问的页面也很重要。因此,如果页面 X(会话 ID 为 abc)包含指向页面 1、2 和 3 的链接,则任何使用会话 ID abc 访问页面 4 的尝试, 将失败并终止会话。

This will force the user to always have one single session track, and always follow the logic on the site. Any attempt to go forward, back, using history or log entires, or opening multiple windows or tabs, will fail and logout the user in all windows, tabs and devices.

这将强制用户始终只有一个会话跟踪,并始终遵循站点上的逻辑。任何前进、后退、使用历史记录或日志整体或打开多个窗口或选项卡的尝试都将失败,并会在所有窗口、选项卡和设备中注销用户。

All this can be completely implemented on server-side, without any client-side logic.

所有这些都可以在服务器端完全实现,无需任何客户端逻辑。

回答by Anthony Vipond

I wrote this to stop a call center page from being accessed in multiple tabs. It works well and is purely client-side. Just update the else ifpart to do what you want if it detects a new tab.

我写这个是为了阻止在多个选项卡中访问呼叫中心页面。它运行良好,纯粹是客户端。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);
}