阻止人们将我的网站加载到多个标签上

Jas*_*son 21 javascript browser cookies tabs

我希望用户只能在浏览器中的一个选项卡上浏览我的网站.如何才能做到这一点?我会使用javascript和cookies吗?

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

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

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

小智 23

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

因此,如果我有三个选项卡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!");
}
Run Code Online (Sandbox Code Playgroud)

注意:这是IE9 +

希望这可以帮助.

  • 如果您复制一个选项卡,会话存储也将位于新选项卡中。_[链接](/sf/ask/1502458751/)_ (3认同)
  • @DanielASathishKumar 所有 JS 都应该在页面中,您应该在页面加载时运行 register_tab_GUID()。 (2认同)

Dan*_*Jin 15

扩展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');
    }
});
Run Code Online (Sandbox Code Playgroud)


reh*_*001 10

更新 - 2020

客户端实现:

我们可以使用广播频道 API,它允许跨浏览上下文(窗口、选项卡、框架或 iframe)进行通信,前提是这两个上下文来自同一来源。

检测从第一个选项卡加载网站的第二个选项卡的简单实现:

    //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 === 'another-tab') {
        // message received from 2nd tab
        alert('Cannot open multiple instances');
      }
    });
Run Code Online (Sandbox Code Playgroud)

如果第一个选项卡处于脱机状态并且正在加载第二个选项卡,则不会使用localStoragecookies它甚至可以工作。

注意:Safari 和 IE11 尚不支持此功能:(

记下它的浏览器兼容性

但是,有一个polyfill可以完成这项工作。

  • 我必须使用 `(msg.data === 'another-tab')` 才能使其正常工作。 (3认同)

Sep*_*ehr 9

EDIT2:

这个答案中提到的确切的事情,你需要2个ID:

  1. 一个随机的
  2. 一致的(实际上这将是我们的SSID,因为您限制单个浏览器的选项卡,最好从浏览器的唯一参数生成表单)

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

如果是,请为客户生成一对新的,如果不是,请拒绝他.


最重要的两条建议:

  1. 服务器端(更好):为您的所有客户端提供用户名和密码.在首次访问您的网站时请求他们输入他们的凭据.然后在每个其他请求上,检查具有所述凭据的用户是否已登录.
  Client *
         |
         |
      Server ---> Check whether
                  Already logged
                     or not?
                  ______________
                   |          |
                  yes         no
                   |          |
                 permit     reject
                  him        him
  1. 客户端:如果您真的需要对此进行强有力的检查,请使用 evercookiealready-logged-incookie存储在客户端的计算机上.

旁注:请注意,客户端的每次尝试都不安全!客户端应该帮助服务器端,它不应该被用作唯一的安全源.甚至evercookies都可以删除所以,给我的第一个建议去.


编辑:

Evercookie在存储大多数安全的僵尸cookie时确实做得很好,但由于浏览器本身对浏览器来说有点沉重(每次存储cookie需要超过100毫秒),因此不建议在现实世界的网络应用程序中使用它.

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


tim*_*ypa 5

我知道这篇文章很老了,但是如果它对任何人都有帮助,我最近调查了基本上使用localStorage和sessionStorage做同样的事情.

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

显然,你会改变"tab is good","tab is bad"条件来做任何你想要的逻辑.

哦,而且,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';
}
Run Code Online (Sandbox Code Playgroud)