Как отличить сессии в браузере-вкладках?

В веб-приложении, реализованном на Java с использованием JSP и Servlets; если я храню информацию в пользовательском сеансе, эта информация передается со всех вкладок одного и того же браузера. Как отличить сеансы в браузере-вкладках? В этом примере:

<%@page language="java"%>
<%
String user = request.getParameter("user");
user = (user == null ? (String)session.getAttribute("SESSIONS_USER") : user);
session.setAttribute("SESSIONS_USER",user);
%>
<html><head></head><body>
<%=user %>
<form method="post">
User:<input name="user" value="">
<input type="submit" value="send">
</form>
</body></html>

Скопируйте этот код на странице JSP (testpage.jsp), разверните этот файл в существующем контексте веб-приложения на сервере (я использую Apache Tomcat), затем откройте браузер (FF, IE7 или Opera), используя правильный URL-адрес (localhost/context1/testpage.jsp), введите свое имя в поле ввода и отправьте форму. Затем откройте новую вкладку в том же браузере, и тогда вы сможете увидеть свое имя (получить из сеанса) на новой вкладке. Будьте осторожны с кешем браузера, иногда кажется, что этого не происходит, но он находится в кеше, обновите вторую вкладку.

Благодарю.

21 ответ

Вы можете использовать HTML5 SessionStorage (window.sessionStorage). Вы сгенерируете случайный идентификатор и сохраните его в хранилище сеанса на вкладке браузера. Тогда каждая вкладка браузера имеет свой собственный идентификатор.

Данные, сохраненные с помощью sessionStorage, не сохраняются на вкладках браузера, даже если обе вкладки содержат веб-страницы из одного и того же домена. Другими словами, данные внутри sessionStorage ограничиваются не только доменом и каталогом вызывающей страницы, но и вкладкой браузера, в которой содержится страница. Сравните это с файлами cookie сеанса, которые сохраняют данные от вкладки к вкладке.

Вы должны понимать, что сеансы на стороне сервера являются искусственным дополнением к HTTP. Поскольку HTTP не имеет состояния, сервер должен каким-то образом распознать, что запрос принадлежит конкретному пользователю, которого он знает и для которого существует сеанс. Есть 2 способа сделать это:

  • Печенье. Более чистый и популярный метод, но он означает, что все вкладки и окна браузера одним пользователем совместно используют сеанс - IMO, это на самом деле желательно, и я был бы очень раздражен на сайте, который заставил меня войти в систему для каждой новой вкладки, так как я очень интенсивно пользуйтесь вкладками
  • Перезапись URL. К любому URL на сайте добавлен идентификатор сеанса. Это больше работы (вы должны что-то делать везде, где у вас есть внутренняя ссылка на сайт), но позволяет иметь отдельные сеансы на разных вкладках, хотя вкладки, открытые по ссылке, по-прежнему будут совместно использовать сеанс. Это также означает, что пользователь всегда должен входить в систему, когда он заходит на ваш сайт.

Что ты пытаешься делать? Почему вы хотите, чтобы вкладки имели отдельные сессии? Может быть, есть способ достичь цели без использования сессий вообще?

Изменить: для тестирования могут быть найдены другие решения (например, запуск нескольких экземпляров браузера на отдельных виртуальных машинах). Если одному пользователю необходимо одновременно выполнять разные роли, тогда в приложении должна быть реализована концепция "роли", чтобы один логин мог иметь несколько ролей. Вам нужно решить, является ли это более приемлемым, с использованием перезаписи URL или просто с учетом текущей ситуации, поскольку просто невозможно обрабатывать вкладки браузера отдельно с помощью сеансов на основе файлов cookie.

Свойство window.name Javascript - это единственная вещь, которая будет сохраняться в течение всей активности вкладок, но может оставаться независимой (вместо URL-болтовни).

Ты не должен. Если вы хотите сделать такую ​​вещь, либо вам нужно заставить пользователя использовать один экземпляр вашего приложения, записывая URL-адреса на лету, используя идентификатор sessionID (а не sessionid, он не будет работать) id и передавая его в каждом URL-адресе.

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

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

Используйте localStorage (или эквивалентный) и событие хранилища HTML5, чтобы определить, когда новая вкладка браузера изменила, какой пользователь активен. Когда это произойдет, создайте призрачное наложение с сообщением о том, что вы не можете использовать текущее окно (или временно отключите окно, возможно, вы не захотите, чтобы оно было таким заметным). Когда окно вновь обретает фокус, отправьте запись AJAX-запроса в журнал. пользователь вернулся.

Одно предостережение об этом подходе: у вас не может быть никаких обычных вызовов AJAX (то есть тех, которые зависят от вашего сеанса), происходящих в окне, которое не имеет фокуса (например, если у вас был вызов, произошедший после задержки), если только перед этим вы вручную делаете повторный вход в AJAX. Поэтому на самом деле все, что вам нужно сделать, это сначала проверить свою функцию AJAX, чтобы убедиться, что localStorage.currently_logged_in_user_id === window.yourAppNameSpace.user_id, и если нет, сначала войдите в систему через AJAX.

Другое - это условия гонки: если вы можете переключать окна достаточно быстро, чтобы запутать его, вы можете получить последовательность relogin1->relogin2->ajax1->ajax2, причем ajax1 будет создан в неправильном сеансе. Чтобы обойти эту проблему, отправьте запросы AJAX входа в массив, а затем onstorage и перед выполнением нового запроса входа прервите все текущие запросы.

Последнее, на что нужно обратить внимание - это обновления окна. Если кто-то обновляет окно, когда у вас активен, но не выполнен запрос на вход в AJAX, он будет обновлен на имя не того человека. В этом случае вы можете использовать нестандартное событие beforeunload, чтобы предупредить пользователя о потенциальной путанице и попросить его нажать Отмена, в то же время повторно выполняя запрос входа в AJAX. Тогда единственный способ, которым они могут это испортить, - это нажать OK до завершения запроса (или случайно нажать Enter/ пробел, потому что OK - к сожалению, для этого случая - по умолчанию.) Есть другие способы обработки этого случая, например обнаружение нажатий F5 и Ctrl+R/Alt+R, которые будут работать в большинстве случаев, но могут быть сорваны из-за перенастройки сочетания клавиш пользователя или альтернативного использования ОС. Тем не менее, это на самом деле немного крайний случай, и наихудшие сценарии никогда не бывают так плохи: в конфигурации системы чести вы будете зарегистрированы как неправильный человек (но вы можете сделать очевидным, что это случай путем персонализации страниц с цветами, стилями, заметными именами и т. д.); в конфигурации с паролем ответственность лежит на последнем человеке, который ввел свой пароль, чтобы выйти из системы или поделился своим сеансом, или, если этот человек фактически является текущим пользователем, нарушения не существует.

Но, в конце концов, у вас есть приложение "один пользователь на вкладку", которое (будем надеяться) просто действует как надо, без необходимости настраивать профили, использовать IE или переписывать URL-адреса. Убедитесь, что вы четко указываете на каждой вкладке, кто вошел в эту вкладку, хотя...

Я буду честен здесь.,,все вышеперечисленное может быть или не быть правдой, но все это кажется СЛИШКОМ СЛОЖНЫМ или не учитывает знание того, какая вкладка используется на стороне сервера.

Иногда нам нужно применить бритву Оккама.

Вот подход Оккама: (нет, я не Оккам, он умер в 1347 году)

1) назначить уникальный идентификатор браузера для вашей страницы при загрузке.,, тогда и только тогда, когда у окна еще нет идентификатора (поэтому используйте префикс и обнаружение)

2) на каждой вашей странице (используйте глобальный файл или что-то) просто поместите код на место, чтобы обнаружить событие фокуса и / или событие наведения мыши. (Я буду использовать jquery для этой части, для простоты написания кода)

3) в вашей функции фокуса (и / или наведения мыши) установите cookie с окном window.name

4) читать значение cookie со стороны вашего сервера, когда вам нужно прочитать / записать данные, относящиеся к вкладке.

Сторона клиента:

//Events 
$(window).ready(function() {generateWindowID()});
$(window).focus(function() {setAppId()});
$(window).mouseover(function() {setAppId()});


function generateWindowID()
{
    //first see if the name is already set, if not, set it.
    if (se_appframe().name.indexOf("SEAppId") == -1){
            "window.name = 'SEAppId' + (new Date()).getTime()
    }
    setAppId()
}

function setAppId()
{
    //generate the cookie
    strCookie = 'seAppId=' + se_appframe().name + ';';
    strCookie += ' path=/';

    if (window.location.protocol.toLowerCase() == 'https:'){
        strCookie += ' secure;';
    }

    document.cookie = strCookie;
}

сторона сервера (C# - для примера)

//variable name 
string varname = "";
HttpCookie aCookie = Request.Cookies["seAppId"];
if(aCookie != null) {
     varname  = Request.Cookies["seAppId"].Value + "_";
}
varname += "_mySessionVariable";

//write session data 
Session[varname] = "ABC123";

//readsession data 
String myVariable = Session[varname];

Готово.

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

Таким образом, решение было случайных поддоменов.

23423.abc.com
242234.abc.com
235643.abc.com

Поэтому мы попросили нашего системного администратора настроить SSL-сертификаты для *.abc.com, а не для abc.com. Затем, при небольшом изменении кода, каждый раз, когда пользователь пытается войти в систему, он попадает на вкладку со случайным номером субдомена. поэтому каждая вкладка может иметь свою собственную сессию независимо. Также, чтобы избежать любого конфликта, мы разработали случайное число, используя хэш или md5 идентификатора пользователя.

Примечание: решение здесь должно быть сделано на этапе разработки приложения. Было бы сложно спроектировать это позже.

Используйте скрытое поле для передачи идентификатора сеанса.

Для этого на каждой странице должна быть форма:

<form method="post" action="/handler">

  <input type="hidden" name="sessionId" value="123456890123456890ABCDEF01" />
  <input type="hidden" name="action" value="" />

</form>

Каждое действие на вашей стороне, включая навигацию, отправляет форму обратно (установка action при необходимости). Для "небезопасных" запросов вы можете включить другой параметр, например, содержащий значение JSON данных, которые будут отправлены:

<input type="hidden" name="action" value="completeCheckout" />
<input type="hidden" name="data" value='{ "cardNumber" : "4111111111111111", ... ' />

Поскольку нет файлов cookie, каждая вкладка будет независимой и не будет знать о других сессиях в том же браузере.

Много преимуществ, особенно когда речь идет о безопасности:

  • Не зависит от JavaScript или HTML5.
  • По своей сути защищает от CSRF.
  • Не зависит от печенья, поэтому защищает от пуделя.
  • Не уязвим для фиксации сессии.
  • Может предотвратить использование кнопки "назад", что желательно, если вы хотите, чтобы пользователи следовали по заданному пути через ваш сайт (это означает, что могут быть предотвращены логические ошибки, которые иногда могут быть атакованы запросами не по порядку).

Некоторые недостатки:

  • Функциональность кнопки Назад может быть желательной.
  • Не очень эффективно с кэшированием, так как каждое действие - это ПОЧТА.

Дополнительная информация здесь.

В javascript, как я могу однозначно идентифицировать одно окно браузера из другого, которые находятся под тем же идентификатором сессии

По существу используйте window.name. Если он не установлен, установите для него уникальное значение и используйте его. Это будет отличаться для разных вкладок, которые принадлежат одному сеансу.

Я думаю, что вы, вероятно, хотите, чтобы поддерживать состояние навигации по вкладкам, а не специально создавать один сеанс для каждой вкладки. Это именно то, чего достигает платформа Seam с помощью контекста / контекста беседы. Их реализация основывается на том факте, что идентификатор диалога распространяется с каждым запросом и создает понятие диалога на стороне сервера, которое находится между сеансом и запросом. Это позволяет контролировать поток навигации и управление состоянием.

Хотя это в основном нацелено на JSF, посмотрите и проверьте, можете ли вы кое-что почерпнуть из этого: http://docs.jboss.org/seam/latest/reference/en-US/html_single/

Вы можете использовать переписывание ссылок для добавления уникального идентификатора ко всем вашим URL-адресам, когда начинаете с одной страницы (например, index.html/jsp/ любой другой). Браузер будет использовать одни и те же файлы cookie для всех ваших вкладок, поэтому все, что вы помещаете в файлы cookie, не будет уникальным.

Недавно я разработал решение этой проблемы с помощью файлов cookie. Вот ссылка на мое решение. Я также включил пример кода решения с использованием ASP.NET, вы сможете адаптировать его к JSP или сервлетам, если вам нужно.

https://sites.google.com/site/sarittechworld/track-client-windows

Другой подход, который работает, состоит в том, чтобы создать уникальный идентификатор окна и сохранить это значение вместе с идентификатором сеанса в таблице базы данных. Идентификатор окна, который я часто использую, является целым числом (сейчас). Это значение создается, когда окно открывается и переназначается тому же окну, если оно обновляется, перезагружается или передается самому себе. Значения окна (входы) сохраняются в локальной таблице по ссылке. Когда значение требуется, оно получается из таблицы базы данных на основе ссылки id окна / идентификатора сеанса. Хотя этот подход требует локальной базы данных, он практически надежен. Мне было легко использовать таблицу базы данных, но я не вижу причин, по которым локальные массивы не будут работать так же хорошо.

Spring Session поддерживает несколько сеансов в одном браузере. Посмотрите примеры и детали реализации http://docs.spring.io/spring-session/docs/current/reference/html5/guides/users.html

Я решил это следующим образом:

  • Я назначил имя окну, это имя совпадает с ресурсом подключения.
  • плюс 1, чтобы избавиться хранится в куки для подключения вложения.
  • Я создал функцию для захвата всех ответов xmloutput и назначения sid и rid для cookie в формате json. Я делаю это для каждого окна.

вот код:

var deferred = $q.defer(),
        self = this,
        onConnect = function(status){
          if (status === Strophe.Status.CONNECTING) {
            deferred.notify({status: 'connecting'});
          } else if (status === Strophe.Status.CONNFAIL) {
            self.connected = false;
            deferred.notify({status: 'fail'});
          } else if (status === Strophe.Status.DISCONNECTING) {
            deferred.notify({status: 'disconnecting'});
          } else if (status === Strophe.Status.DISCONNECTED) {
            self.connected = false;
            deferred.notify({status: 'disconnected'});
          } else if (status === Strophe.Status.CONNECTED) {
            self.connection.send($pres().tree());
            self.connected = true;
            deferred.resolve({status: 'connected'});
          } else if (status === Strophe.Status.ATTACHED) {
            deferred.resolve({status: 'attached'});
            self.connected = true;
          }
        },
        output = function(data){
          if (self.connected){
            var rid = $(data).attr('rid'),
                sid = $(data).attr('sid'),
                storage = {};

            if (localStorageService.cookie.get('day_bind')){
              storage = localStorageService.cookie.get('day_bind');
            }else{
              storage = {};
            }
            storage[$window.name] = sid + '-' + rid;
            localStorageService.cookie.set('day_bind', angular.toJson(storage));
          }
        };
    if ($window.name){
      var storage = localStorageService.cookie.get('day_bind'),
          value = storage[$window.name].split('-')
          sid = value[0],
          rid = value[1];
      self.connection = new Strophe.Connection(BoshService);
      self.connection.xmlOutput = output;
      self.connection.attach('bosh@' + BoshDomain + '/' + $window.name, sid, parseInt(rid, 10) + 1, onConnect);
    }else{
      $window.name = 'web_' + (new Date()).getTime();
      self.connection = new Strophe.Connection(BoshService);
      self.connection.xmlOutput = output;
      self.connection.connect('bosh@' + BoshDomain + '/' + $window.name, '123456', onConnect);
    }

Я надеюсь помочь тебе

Сохранение метки времени в window.sessionStorage, если она еще не установлена. Это даст уникальное значение для каждой вкладки (даже если URL-адреса одинаковы)

http://www.javascriptkit.com/javatutors/domstorage.shtml

https://developer.mozilla.org/en-US/docs/Web/Guide/API/DOM/Storage

Надеюсь это поможет.

Я вижу много реализаций, которые имеют изменения на стороне клиента для манипулирования файлами cookie идентификатора сессии. Но в общем случае куки с идентификатором сессии должны быть HttpOnly, чтобы java-скрипт не мог получить к ним доступ, иначе это может привести к перехвату сессии через XSS

Если это связано с тем, что на каждой вкладке в вашем приложении будет выполняться отдельный поток, а смешивание обоих потоков вызывает проблемы, то лучше "регионализировать" объекты сеанса, чтобы каждый поток использовал свой регион сеанса.

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

Вам нужно будет сделать

1- сохранить куки для списка аккаунтов

2- опционально хранить куки по умолчанию

3- хранить для каждой учетной записи с индексом, таким как acc1, acc2

4 - введите в URL-адрес что-то, представляющее индекс учетных записей, и если нет, вы выберете индекс по умолчанию, например, gmail mail domain.com/0/some-url >> 0, здесь представлен индекс учетной записи, также вам может понадобиться узнать, как использовать urlwrite

5 - при выборе файла cookie выберите его в соответствии с вашим URL-адресом, представляющим индекс учетной записи.

С уважением

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

Прочитав эти ответы, особенно ответ, который дал Майкл Боргвардт, я понял, какой рабочий процесс должен существовать:

  1. Если пользователь переходит к экрану входа, проверьте существующий сеанс. Если существует, обойдите экран входа в систему и отправьте их на экран приветствия.
  2. Если пользователь (в моем случае) переходит к экрану регистрации, проверьте существующий сеанс. Если он существует, сообщите пользователю, что вы собираетесь выйти из сеанса. Если они согласны, выйдите из системы и начните регистрацию.

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

Теперь, чтобы решить проблему тестирования. Единственный жизнеспособный подход заключается в использовании директив препроцессора для определения необходимости использования сеансов без файлов cookie. Видите, благодаря созданию конкретной конфигурации для конкретной среды, я могу сделать некоторые предположения об окружающей среде и для чего она используется. Это позволило бы мне технически подключить одновременно двух пользователей, и тестировщик мог бы протестировать несколько сценариев из одного сеанса браузера, даже не выходя из какого-либо из этих сеансов сервера.

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

Так что я думаю, что должен сказать, что в конечном итоге это плохая идея.

How to differ sessions in browser-tabs?

Самый простой способ различать сеансы на вкладках браузера - запретить настройку файлов cookie в вашем конкретном домене. Таким образом, вы можете иметь отдельные сессии из отдельных вкладок. Скажем, вы запрещаете куки с этого домена: www.xyz.com. Вы открываете Tab 1, авторизуетесь и начинаете просмотр. Затем вы открываете Tab 2, и вы можете войти в систему как тот же пользователь или другой; в любом случае у вас будет сеанс, отдельный от Tab 1. И так далее.

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

Другие вопросы по тегам