Как запретить пользователю иметь несколько экземпляров одного и того же веб-приложения

Мне интересно, можно ли определить, имеет ли пользователь уже веб-браузер, открытый для веб-приложения, над которым я работаю. Кажется, что они могут открывать несколько экземпляров одного и того же веб-приложения и нажимать на кнопки, чтобы читать информацию, которую они использовали раньше, чтобы войти в экран ввода, в котором они сейчас работают.

Что происходит, так это то, что он, кажется, испортил переменные сеанса, а затем пользователь обновит свою предыдущую работу своей новой работой. Или они удалят свою предыдущую работу вместе или знают...

ИЗМЕНИТЬ Я уже это делал с веб-приложениями онлайн-банкинга. Если вы уже вошли в систему, новое окно сообщит вам, что у вас уже открыто приложение. В моем случае пользователю не нужно входить в систему.

Есть ли простой способ определить, есть ли у них окно браузера, открытое для веб-приложения, и если да, просто закройте браузер или отобразите другую страницу, чтобы сообщить им, что они могут иметь только один доступ за один раз?

Спасибо

Ответ 1

Во-первых, нет, нет, и, во-вторых, вы не должны пытаться.

Стратегия всплывающих окон не будет работать (и будет раздражать пользователей). У меня в браузере установлен "Открыть окна как вкладки", и я выбираю, нужно ли разделить его в другое окно. Или в некоторых случаях, нужно ли запускать другой браузер, а не только другой экземпляр одного и того же, чтобы отобразить другую страницу на одном сайте.

И наоборот, идентификатор мини-сеанса завершится неудачно, потому что сервер не может отслеживать, является ли запрос от того же пользователя, что и существующий сеанс. Несколько человек могут использовать одну и ту же машину, даже с тем же именем пользователя; или один человек может иметь несколько отдельных сеансов входа в систему на одной или нескольких машинах.

Просто выберите переменные протокола и "Сессия" и убедитесь, что последние зафиксированные изменения - это те, которые сохраняются.

Ответ 2

Вы можете назначить идентификатор мини-сессии каждому экземпляру формы ввода, а затем использовать AJAX для ping-сервера с этим идентификатором. Если пользователь пытается запросить ту же форму при наличии активного идентификатора, он должен отобразить сообщение об ошибке. Если сервер не слышит пинг в течение определенного времени, истекает мини-сеанс. (Это, по сути, очень простая стратегия блокировки)

Ответ 3

Все, что вам нужно сделать, это присвоить значение как значению управления скрытым входом, так и переменной сеанса в событии 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

Я использую трюк открытия нового окна с определенным идентификатором и всегда слежу за тем, чтобы любая страница открывалась всегда, используя это окно.

С другой стороны, у них должен быть отключен блокировщик всплывающих окон для вашего сайта. Он хорошо работает для сайтов компании.

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

Есть ли простой способ определить, есть ли у них окно браузера, открытое для веб-приложения, и если да, просто закройте браузер или отобразите другую страницу, чтобы сообщить им, что они могут иметь только один доступ за один раз?

Короче говоря, №. Не записывая какой-либо элемент управления ActiveX, нет кода, который вы могли бы написать, который может остановить пользователя от открытия (отдельный экземпляр) IE/FF и т.д., И один экземпляр обнаружит другой.

Ответ 6

Я бы предложил вам хэш ViewState для страницы и сохранить его в переменной сеанса до того, как он будет возвращен как ответ.

Затем для запроса сначала проверьте хэш возвращаемого ViewState на тот, который у вас есть в переменной сеанса, и если они не совпадают, не обрабатывайте никаких изменений на странице и не вывешивайте уведомление своему пользователю или не перенаправляйте их на страницу с ошибкой.

Два метода класса Page, которые вы хотите переопределить:

protected override object LoadPageStateFromPersistenceMedium()

protected override void SavePageStateToPersistenceMedium(object viewState)

Ответ 7

Мы внедрили решение этой проблемы на slicethepie.com. Пользователям (или "скаутам" ) разрешено открывать только одно окно браузера, чтобы они прослушивали музыку, которую они платят, чтобы просмотреть.

Когда скаут запрашивает первый трек, который будет рассмотрен на их сеансе разведки, мы устанавливаем новый Guid в своей учетной записи и возвращаем этот "ключ" вместе с деталями трека, который им дается для просмотра. Бывает, что получателем этого ключа является флеш-ролик, но это необязательно. Ключ повторно отправляется вместе с обзором разведчика, и мы проверяем, соответствует ли он сохраненному ключу. Если это не так, они запустили новый сеанс разведки в новом окне.

Я не подразумеваю, что этот метод является надежным, но он может быть легко адаптирован к вашим потребностям.

Ответ 8

Вы можете остановить функциональность страницы, когда пользователь открыл другую вкладку или другое окно или даже другой браузер

  $(window).blur(function(){

// code to stop functioning or close the page  

});

Ответ 9

Я частично решил проблему таким образом. Мое приложение запрашивает у пользователей аутентификацию... поэтому они вводят имя пользователя и пароль. Когда они нажимают кнопку отправки, я сохраняю оба данных в переменной сеанса, т.е. Я храню имя пользователя в сеансе ( "LoginName" ).

Итак, вот трюк: просто проверьте, имеет ли Session ( "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

Вы можете проверить, имеет ли пользователь более одного активного сеанса. Это может сработать, но может быть проблематичным, поскольку нет хорошего способа определить, действительно ли сеанс активен или нет.

Ответ 11

Как уже упоминалось, вы не можете помешать пользователю начать новый сеанс, не прибегая к ActiveX или другой гадости. Основная проблема заключается в том, что вам не удастся узнать, закрыл ли пользователь старое окно браузера или оставил его открытым.

Однако вы можете сделать недействительным предыдущий сеанс, как только пользователь войдет в новый (немного похоже на то, как могут работать клиенты мгновенных сообщений).

При каждом входе в систему назначьте новый GUID пользователю в своей базе данных. Также сохраните этот GUID в кеше сеанса (не нужно отправлять его туда и обратно на страницы, которые в любом случае не будут работать для запросов GET). В каждом запросе страницы сравните GUID, назначенный пользователю в базе данных с GUID в кеше сеанса. Если они не совпадают, верните ответ "Вы вошли в систему из другого места".

Обновление Я был слишком быстрым на триггере. Это не мешает сценарию, когда пользователь открывает несколько вкладок/окон в одном и том же браузере. Таким образом, вам нужно было бы объединить это решение с предложением Дейва Андерсона для хранения хеш-представления ViewState (или просто GUID), чтобы только последняя страница в сеансе разрешала отправлять сообщения назад.

Обновление безопасности Кроме того, вы можете полагаться только на эту инфраструктуру как удобство для пользователя, так как она не защищена. Любой полуподобный хакер сможет обойти эти меры.

Ответ 12

Если кто-то скопирует URL-адрес веб-сайта и вставляет его в новое окно или вкладку, история браузера для этого окна/вкладки будет пустой... так что вы можете использовать javascript и проверить историю.

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

Теперь вы можете предложить пользователю закрыть вкладку или отключить эту страницу (например, создав все элементы disabled).

Ответ 13

У меня была такая же проблема, и я решил ее использовать с помощью сетевых сокетов (я использую pusher с php). Когда пользователь приземляется на странице, я устанавливаю переменную:

var firstTimeHere = true;

Затем я создал функцию websocket в javascript, которая имеет идентификатор пользователя как часть имени функции, например:

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

Затем я использую ajax для запроса сервера, на котором запущена php-сторона веб-узла:

$this->trigger("pingUser123");

Websocket запускает все экземпляры javascript этой функции в любом количестве окон на любом количестве браузеров и машин. Оттуда я вижу, если это первый раз, когда этот браузер был pinged. если это единственный экземпляр, если нет второго.

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

Ответ 14

 <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>

Ответ 15

У меня есть код 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()

Ответ 16

Вы можете сделать это с помощью window.name. В java script window.name имеет пустое значение на каждой новой вкладке. Задайте значение window.name на странице входа и сохраните сессию.

window.name = Math.random() + "_YourApplication"

Теперь проверьте это окно .name на главной странице/странице макета. Выйдите из системы, если она содержит несколько вкладок.

 if (!window.name || window.name != '@Session["WindowName"]') {
    //Log Off code
 }