我希望用户从浏览器中的仅一个标签浏览我的网站。如何才能做到这一点?我会使用javascript和cookies吗?
例如,我有一个网站: www.example.com - 我希望我的客户只能从一个标签中访问该网站浏览器。如果他们打开另一个选项卡并加载站点(或站点的子页面) - 我想要一个警告“无法打开多个实例”,然后将它们重定向到错误页面。
需要注意的事项 - 如果用户将地址从 www.example.com/action/door/mine.aspx 更改为 www .example.com - 应该可以正常工作,因为用户位于相同的(原始)标签中。
任何帮助将不胜感激。提前谢谢。
答案 0 :(得分:20)
我为此创建了一个简单的解决方案。母版页布局创建选项卡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!");
}
注意:这是IE9 +
希望这有帮助。
答案 1 :(得分:9)
更新-2020
客户端实施:
我们可以利用Broadcast Channel 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');
}
});
这不使用localStorage
或cookies
,即使第一个标签页处于脱机状态且第二个标签页正在加载,它也可以使用。
注意:Safari和IE11尚不支持此功能:(
在其browser compatibility上记笔记。
但是,有polyfill个可用的文件。
答案 2 :(得分:7)
<强> EDIT2:强>
这是this answer提到的确切内容,你需要2个ID:
您可以从浏览器的用户代理生成一致的或从服务器端获取它。将它们都存储在服务器端
将随机存储在window.name
属性中,该属性是特定于制表符的
每1~2秒向您的服务器发送一次包含一致ID和随机ID的心跳。如果服务器无法接收心跳,它会清理数据库并取消注册死客户端。
在每个浏览器的请求中,检查window.name
的值。如果丢失,请检查服务器端是否关闭了上一个选项卡(从数据库中清除)。
如果是,如果没有,为客户生成一对新的,拒绝他。
<小时/> 最重要的两条建议:
Client * | | Server ---> Check whether Already logged or not? ______________ | | yes no | | permit reject him him
already-logged-in
Cookie。 旁注: 请注意,客户端的每次尝试都不安全!客户端应该帮助服务器端,它不应该被用作唯一的安全源。 even evercookies can be deleted所以,请给我第一个建议。
<小时/> 修改强>
Evercookie在存储大多数安全的僵尸cookie时确实做得很好但是因为浏览器本身对浏览器来说有点沉重(每次存储cookie需要超过100毫秒),所以不建议在现实世界中使用它网络应用。
如果您使用服务器端解决方案,请使用这些:
答案 3 :(得分:5)
我知道这篇文章很老了,但是如果它对任何人都有帮助,我最近调查了基本上使用localStorage和sessionStorage做同样的事情。
类似Anthony's answer,它设置一个间隔以确保原始标签保持条目新鲜,这样如果浏览器崩溃或以某种方式关闭而不调用卸载事件(包含在注释中但不包含代码的一部分)出于测试目的),在应用程序在新的浏览器窗口中正常运行之前,只会有一个短暂的延迟。
显然,你会改变“tab is good”,“tab is bad”条件来做你想做的任何逻辑。
哦,而且,createGUID方法只是一个实用程序,可以使会话标识符唯一...它是从this answer到之前的问题(想要确保我没有被认可)
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';
}
答案 4 :(得分:1)
为什么你想这样做?
可以尝试做一些丑陋的黑客攻击,但结果会是:没有方式可以完全抑制此行为。
JavaScript无法解决这个问题,因为用户总是有可能在浏览器中禁用了JavaScript,或者只允许某个子集。
用户可以打开新浏览器,使用其他计算机等一次访问多个页面。
但更重要的是:
此外,您的网站将是唯一具有此行为的网站,因此这会让使用您网站的所有人感到困惑,因为它不像网站那样有效。每个试图打开第二个标签的人都会想:“这很奇怪。这个网站很糟糕,因为它与网站不一样。我不会再来了!” ; - )
答案 5 :(得分: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
答案 6 :(得分:0)
解决此问题的最佳方法是拥有一次性会话ID。
例如,每个页面都包含一个会话ID,该会话ID对一次访问有效,是唯一的,也是随机的。 点击任何一个链接时,它将使用&amp;使会话ID无效,新页面将有一个新的会话ID。
这将强制用户始终浏览最新的窗口或选项卡,还可以防止会话通过网络窃取。 任何重用旧会话ID的尝试都应立即杀死该用户的活动会话ID。
在会话管理系统中存储哪些页面可从第X页访问也很重要。因此,如果页面X(会话ID为abc)包含指向第1页,第2页和第3页的链接,则任何尝试访问第4页的页面都是如此。会话ID abc,将失败并终止会话。
这将强制用户始终拥有一个会话轨道,并始终遵循网站上的逻辑。任何前进,后退,使用历史记录或日志输入或打开多个窗口或选项卡的尝试都将失败,并在所有窗口,选项卡和设备中注销用户。
所有这些都可以在服务器端完全实现,没有任何客户端逻辑。
答案 7 :(得分: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);
}