如何防止用户拥有Same Web应用程序的多个实例

时间:2009-02-17 17:01:52

标签: asp.net javascript visual-studio-2005

我想知道是否可以确定用户是否已经打开了我正在处理的Web应用程序的Web浏览器。看起来他们可以打开同一个Web应用程序的多个实例,然后单击按钮来读取他们之前用过的信息,进入他们当前正在处理的输入屏幕。

虽然它似乎搞砸了Session变量然后用户将用他们的新工作更新他们以前的工作。或者他们会一起删除他们以前的作品或谁知道......

修改 我之前已经看过网上银行网络应用程序。如果您已经登录,新窗口将告知您已经打开了应用程序。就我而言,用户无需登录。

是否有一种简单的方法可以确定他们是否已经在Web应用程序中打开了浏览器窗口,如果是这样,只需关闭浏览器或显示不同的页面,让他们知道他们一次只能打开1个? / p>

由于

15 个答案:

答案 0 :(得分:8)

首先,没有,第二,你不应该尝试。

弹出窗口策略不起作用(并会使用户烦恼)。我将浏览器设置为“以窗口打开窗口”,然后选择是否将其中一个分割为另一个窗口。或者在某些情况下,是否要运行不同的浏览器 - 而不仅仅是同一个浏览器的另一个实例 - 在同一站点上显示另一个页面。

相反,迷你会话ID将失败,因为服务器无法跟踪请求是否来自与现有会话相同的用户。有些人可能正在使用同一台机器,即使使用相同的用户名;或者一个人可能在一台或多台机器上有几个单独的登录会话。

只需将您的协议与“会话”变量进行排序,并确保最后提交的更改是持久的更改。

答案 1 :(得分:3)

  

是否有一种简单的方法可以确定他们是否已经在Web应用程序中打开了浏览器窗口,如果是这样,只需关闭浏览器或显示不同的页面,让他们知道他们一次只能打开1个? / p>

简而言之,号 在没有编写某种activeX控件的情况下,没有可编写的代码可以阻止用户打开(单独的)IE / FF等实例并让一个实例检测到另一个实例。

答案 2 :(得分:3)

您可以为输入表单的每个实例分配一个“迷你会话”ID,然后使用AJAX使用该ID ping服务器。如果用户在有活动ID时尝试请求相同的表单,则应显示错误消息。如果服务器在一段时间内没有听到ping,则使迷你会话到期。 (这基本上是一种非常简单的锁定策略)

答案 3 :(得分:3)

您所要做的就是为Hidden Input控件的值和Page Load事件中的会话变量以及回发分配值,并根据会话变量中的值检查局部变量的值。如果值不匹配,您可以将用户重定向到登录页面或告诉他们该页面的会话不再有效等的页面。

示例:

Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
    Try
        If Not IsPostBack Then
            'SET LOCAL VARIABLE AND SESSION VARIABLE TO A UNIQUE VALUE
            'IF THE USER HAS CHANGED TABS THIS WILL CHANGE THE VALUE OF THE SESSION VARIABLE
            Me.HiddenInput.value = New Guid().ToString
            Me.Session.Add("PageGuid", Me.HiddenInput.value)

        Else 
            'ON POSTBACK, CHECK TO SEE IF THE USER HAS OPENED A NEW TAB
            'BY COMPARING THE VALUE OF THE LOCAL VALUE TO THE VALUE OF THE SESSION VARIABLE

            If me.HiddenInput.value <> CType(Session("PageGuid"), String) Then

                'THE VALUES DO NOT MATCH, MEANING THE USER OPENED A NEW TAB.
                'REDIRECT THE USER SOMEWHERE HARMLESS

                Response.Redirect("~/Home.aspx")

            Else

                'THE VALUES MATCH, MEANING THE USER HAS NOT OPENED A NEW TAB
                'PERFORM NORMAL POSTBACK ACTIONS

                ...

            End If
        End If
    Catch ex As Exception
        Me.Session.Add("ErrorMessage", BusinessLogic.GetErrorMessage(ex))
        Me.Response.Redirect("~/ErrorPage.aspx", False)
    End Try
End Sub

答案 4 :(得分:2)

我使用打开具有特定ID的新窗口的技巧,并始终确保打开的任何页面始终使用该窗口。

不利的一面是,他们必须为你的网站关闭弹出窗口拦截器。它适用于公司网站。

if (useOwnWindow && window.name != 'YourAPP'){
    var w = window.open(document.location, 'YourAPP', 'toolbar=no,status=yes,resizable=yes,scrollbars=yes');
    if (w==null){
        alert("Please turn off your pop-up blocker");
    }else{
        window.open('','_parent','');
        self.opener="";
        self.close();
    }
 }

如果开发人员使用了useOwnWindow标志,那么我们可以多次打开它

答案 5 :(得分:1)

我建议您为页面散列ViewState,并在将其作为Response返回之前将其存储在会话变量中。

然后,对于请求,首先检查返回的ViewState的哈希值与会话变量中的哈希值,如果它们不匹配,则不处理页面上的任何更改并向用户显示通知或重定向它们到错误页面。

您要覆盖的Page类的两种方法是;

protected override object LoadPageStateFromPersistenceMedium()

protected override void SavePageStateToPersistenceMedium(object viewState)

答案 6 :(得分:1)

我们已经在slicethepie.com上实现了这个问题的解决方案。用户(或“侦察员”)只允许一次打开一个浏览器窗口,以确保他们听取他们付费的音乐进行审核。

当侦察员要求第一首曲目在他们的侦察会话中进行审核时,我们会在他们的帐户上设置一个新的Guid并返回此“密钥”以及他们要审核的曲目的详细信息。碰巧这个键的收件人是一部flash电影,但并非必须如此。密钥与侦察员的评论一起重新提交,我们检查它是否与保存的密钥匹配。如果没有,他们就会在新窗口中开始新的侦察会议。

我并不是说这种方法是万无一失的,但它可以很容易地适应你的需求。

答案 7 :(得分:1)

正如其他人所提到的,你不能阻止用户在不诉诸ActiveX或其他恶意的情况下开始新的会话。基本问题是您无法知道用户是关闭旧浏览器窗口还是将其保持打开状态。

然而,您可以做的是,一旦用户登录到新的会话,就会使之前的会话无效(类似于Instant Messaging客户端的行为方式)。

在每次登录时,为数据库中的用户分配新的GUID。还将此GUID存储在会话缓存中(无需将其传送到页面,无论如何都不适用于GET请求)。在每个页面请求上,将分配给数据库中用户的GUID与会话高速缓存中的GUID进行比较。如果它们不匹配,请返回“您已从其他地方登录”的响应。

更新我的触发器有点太快了。这不会阻止用户在同一浏览器进程中打开多个选项卡/窗口的情况。因此,您必须将此解决方案与Dave Anderson建议用于存储ViewState哈希(或简称GUID),以便只允许会话中最后一个提供的页面回发。

安全更新此外,您只能依赖此框架以方便用户,因为它不安全。任何一半体面的黑客都能够绕过这些措施。

答案 8 :(得分:1)

当用户打开另一个标签页或其他窗口或甚至其他浏览器时,您可以停止页面功能

  $(window).blur(function(){

// code to stop functioning or close the page  

});

答案 9 :(得分:1)

我用这种方式部分地解决了这个问题。 我的应用程序要求用户进行身份验证...因此他们输入用户名和密码。当他们点击提交按钮时,我将两个数据存储到会话变量中,即我将用户名存储到会话中(“LoginName”)。

所以这就是诀窍:在登录页面的页面加载事件中,测试会话(“LoginName”)是否具有用户输入之前的值。如果是这样,这是一个多会话,您可以停止用户或其他任何内容。

像(我使用VB):

    If Not Page.IsPostBack Then
       Try
          If Session("LoginName") <> "" Then
             Response.Redirect("www.mysite.com")
             Exit Sub
          End If
       Catch ex As Exception
          ' Do something
       End Try

PRO:非常简单

CON:用户可以复制内页的地址并将其粘贴到同一浏览器的另一个标签中...这就是为什么我说“部分”

答案 10 :(得分:0)

您可以检查用户是否有多个活动会话。这可能有用,但可能会有问题,因为没有好的方法来确定会话是否真的有效。

答案 11 :(得分:0)

如果有人复制网站网址并将其粘贴到新窗口或标签中,该窗口/标签的浏览器历史记录将为空...这样您就可以使用javascript并查看历史记录了。

if (history.length == 1) {  //0 for IE, 1 for Firefox
    // This is a new window or a new tab.
}

现在,您可以提示用户关闭标签页,或禁用该页面(例如,通过制作所有元素disabled)。

答案 12 :(得分:0)

我遇到了同样的问题并使用网络套接字解决了它(我使用pusher与php)。当用户登陆页面时,我设置了一个变量:

var firstTimeHere = true;

然后我在javascript中创建了一个websocket函数,其中userid作为函数名称的一部分,如:

var pingUser123 = function(){...}; 

然后我使用ajax来查询触发websocket的php端的服务器:

$this->trigger("pingUser123");

websocket在任意数量的浏览器和机器上的任意数量的窗口中触发该函数的所有javascript实例。从那里我可以看到它是否是第一次浏览此浏览器。如果是的话,它是唯一的例子,如果没有,那就是第二个。

var pingUser123 = function(){
    if(firstTimeHere){
       firstTimeHere = false;
    }else{
        app.stop();
        alert("Only one window at a time please.");
    }
};

答案 13 :(得分:0)

 <script type="text/javascript">
        window.onload = function(){
        if (document.cookie.indexOf("_instance=true") === -1) {
        document.cookie = "_instance=true";
        // Set the onunload function
        window.onunload = function(){
            document.cookie ="_instance=true;expires=Thu, 01-Jan-1970 00:00:01 GMT";
        };
        // Load the application
        }
        else {
             alert(" Security Alerts.You Are Opening Multiple Window. This window will now close.");
             var win = window.open("about:blank", "_self"); win.close();
        // Notify the user
        }
        };
    </script>

答案 14 :(得分:0)

我有一个js代码,此代码仅使您拥有页面的一个实例,而仅在运行此js代码的页面中具有。示例:如果将js代码放在page1.html和page2.html中,则该代码将允许page1或page2的一个实例。如果仅将js放在page1中,则page1将只有一个实例,而page2将具有多个实例。

只需复制并粘贴到您的网站中,然后更改逻辑的最后几行即可。

代码:

var statusWindow = document.getElementById('status');

(function (win) {
    //Private variables
    var _LOCALSTORAGE_KEY = 'WINDOW_VALIDATION';
    var RECHECK_WINDOW_DELAY_MS = 100;
    var _initialized = false;
    var _isMainWindow = false;
    var _unloaded = false;
    var _windowArray;
    var _windowId;
    var _isNewWindowPromotedToMain = false;
    var _onWindowUpdated;


    function WindowStateManager(isNewWindowPromotedToMain, onWindowUpdated) {
        //this.resetWindows();
        _onWindowUpdated = onWindowUpdated;
        _isNewWindowPromotedToMain = isNewWindowPromotedToMain;
        _windowId = Date.now().toString();

        bindUnload();

        determineWindowState.call(this);

        _initialized = true;

        _onWindowUpdated.call(this);
    }

    //Determine the state of the window 
    //If its a main or child window
    function determineWindowState() {
        var self = this;
        var _previousState = _isMainWindow;

        _windowArray = localStorage.getItem(_LOCALSTORAGE_KEY);

        if (_windowArray === null || _windowArray === "NaN") {
            _windowArray = [];
        }
        else {
            _windowArray = JSON.parse(_windowArray);
        }

        if (_initialized) {
            //Determine if this window should be promoted
            if (_windowArray.length <= 1 ||
               (_isNewWindowPromotedToMain ? _windowArray[_windowArray.length - 1] : _windowArray[0]) === _windowId) {
                _isMainWindow = true;
            }
            else {
                _isMainWindow = false;
            }
        }
        else {
            if (_windowArray.length === 0) {
                _isMainWindow = true;
                _windowArray[0] = _windowId;
                localStorage.setItem(_LOCALSTORAGE_KEY, JSON.stringify(_windowArray));
            }
            else {
                _isMainWindow = false;
                _windowArray.push(_windowId);
                localStorage.setItem(_LOCALSTORAGE_KEY, JSON.stringify(_windowArray));
            }
        }

        //If the window state has been updated invoke callback
        if (_previousState !== _isMainWindow) {
            _onWindowUpdated.call(this);
        }

        //Perform a recheck of the window on a delay
        setTimeout(function () {
            determineWindowState.call(self);
        }, RECHECK_WINDOW_DELAY_MS);
    }

    //Remove the window from the global count
    function removeWindow() {
        var __windowArray = JSON.parse(localStorage.getItem(_LOCALSTORAGE_KEY));
        for (var i = 0, length = __windowArray.length; i < length; i++) {
            if (__windowArray[i] === _windowId) {
                __windowArray.splice(i, 1);
                break;
            }
        }
        //Update the local storage with the new array
        localStorage.setItem(_LOCALSTORAGE_KEY, JSON.stringify(__windowArray));
    }

    //Bind unloading events  
    function bindUnload() {
        win.addEventListener('beforeunload', function () {
            if (!_unloaded) {
                removeWindow();
            }
        });
        win.addEventListener('unload', function () {
            if (!_unloaded) {
                removeWindow();
            }
        });
    }

    WindowStateManager.prototype.isMainWindow = function () {
        return _isMainWindow;
    };

    WindowStateManager.prototype.resetWindows = function () {
        localStorage.removeItem(_LOCALSTORAGE_KEY);
    };

    win.WindowStateManager = WindowStateManager;
})(window);

var WindowStateManager = new WindowStateManager(true, windowUpdated);

function windowUpdated() {
    //"this" is a reference to the WindowStateManager
    //statusWindow.className = (this.isMainWindow() ? 'main' : 'child');
    if (this.isMainWindow() === false) {
        //alert("This is not the main window")
        location.href = "yourpage.html";


    }
    else {
        //alert("This is the main window")
    }

}
//Resets the count in case something goes wrong in code
//WindowStateManager.resetWindows()