如何在浏览器选项卡中区分会话?

在使用 JSP 和 Servlet 在 Java 中实现的 Web 应用程序中; 如果我在用户会话中存储信息,这些信息将从同一个浏览器的所有选项卡中共享。如何在浏览器选项卡中区分会话? 在这个例子中:

<%@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) ,将这个文件部署到服务器上一个 Web 应用程序的现有上下文中(我使用 Apache Tomcat) ,然后使用正确的 URL (localhost/context1/testpage.jsp)打开一个浏览器(FF、 IE7或 Opera) ,在输入中键入你的名字并提交表单。然后在同一个浏览器中打开一个新的选项卡,然后您可以在新的选项卡上看到您的名字(从会话中获取)。小心浏览器-缓存,有时似乎没有发生,但它在缓存中,刷新第二个标签。

谢谢。

274033 次浏览

当你从一个页面开始(比如 index.html/jsp/whatever)时,你可以使用链接重写来为你所有的 URL 添加一个唯一标识符。浏览器将使用相同的 Cookie 为您的所有标签,所以您放入 Cookie 的一切将 没有是唯一的。

你不应该。 如果你想做这样的事情,你需要强制用户使用你的应用程序的一个实例,通过动态编写 URL,使用类似的 sessionID (而不是 sessionID,它不会工作) ,并在每个 URL 中传递它。

我不知道你为什么需要它,但除非你需要一个完全不可用的应用程序,不要这样做。

您必须认识到服务器端会话是 HTTP 的一个人工附加组件。由于 HTTP 是无状态的,因此服务器需要以某种方式识别请求属于它所知道的特定用户,并且有一个会话用于该用户。有两种方法可以做到这一点:

  • 饼干。这是一种更简洁、更流行的方法,但是这意味着所有的浏览器标签和窗口由一个用户共享会话-IMO 这实际上是可取的,我会非常恼火的网站,让我登录每一个新的标签,因为我使用标签非常密集
  • URL 重写。网站上的任何 URL 都附加了一个会话 ID。这需要做更多的工作(你必须在你有网站的地方做一些事情——内部链接) ,但是可以在不同的选项卡中有不同的会话,尽管通过链接打开的选项卡仍然会共享会话。这也意味着当用户访问您的站点时,他总是必须登录。

你到底想干什么?为什么要让制表符有单独的会话?也许有一种方法可以实现你的目标,而不需要使用会议?

编辑: 对于测试,可以找到其他解决方案(例如在不同的 VM 上运行多个浏览器实例)。如果一个用户需要同时扮演不同的角色,那么应用程序中应该处理“角色”概念,这样一个登录名可以有多个角色。您必须决定是使用 URL 重写,还是仅仅接受当前的情况,因为不可能用基于 cookie 的会话单独处理浏览器标签。

我想出了一个新的解决方案,虽然有一点点开销,但是目前看来还只是一个原型。一个假设是,您正处于一个荣誉系统环境中进行登录,尽管这可以通过在切换标签时需要密码进行调整。

使用 localStorage (或等效的)和 HTML5存储事件来检测新的浏览器选项卡何时切换了哪个用户处于活动状态。当这种情况发生时,创建一个重叠的消息,说你不能使用当前窗口(或者暂时禁用窗口,你可能不希望它这么显眼。)当窗口重新获得焦点时,发送一个 AJAX 请求,将用户登录回来。

这种方法的一个警告是: 你不能让任何普通的 AJAX 调用(例如,那些依赖于你的会话的调用)发生在一个没有焦点的窗口中(例如,如果你有一个调用发生在延迟之后) ,除非你在此之前手动进行一个 AJAX 重新登录调用。所以你真正需要做的就是首先检查你的 AJAX 函数,确保 localStorage.current _ log _ in _ user _ id = = window.yourAppNameSpace.user _ id,如果没有,首先通过 AJAX 登录。

另一个是竞争条件: 如果您切换窗口的速度足够快,以至于混淆了它,那么您可能会得到一个 relogin1-> relogin2-> ajax1-> ajax2序列,而 ajax1是在错误的会话下生成的。为了解决这个问题,可以将登录 AJAX 请求推送到一个数组中,然后在存储上并在发出新的登录请求之前,中止所有当前请求。

最后要注意的是窗户刷新。如果有人刷新了窗口,而您的 AJAX 登录请求处于活动状态,但没有完成,那么将以错误的人的名字刷新该窗口。在这种情况下,您可以使用非标准的 before unload 事件来警告用户可能出现的混淆,并要求他们单击 Cancel,同时重新发出 AJAX 登录请求。然后,他们修改它的唯一方法就是在请求完成之前单击 OK (或者不小心点击回车/空格键,因为 OK 是——不幸的是,在这种情况下——默认值)还有其他方法可以处理这种情况,比如检测 F5和 Ctrl + R/Alt + R 压力机,这些方法在大多数情况下都可以工作,但是可能会受到用户快捷键重新配置或其他操作系统使用的阻碍。然而,这在现实中是一个边缘情况,最糟糕的情况从来没有那么糟糕: 在一个荣誉系统配置中,你会作为错误的人登录(但是你可以通过个性化页面的颜色,样式,突出显示的名字等,很明显这是一个错误的情况) ; 在密码配置中,责任在于最后一个输入密码的人已经登出或共享他们的会话,或者如果这个人实际上是当前用户,那么就不存在漏洞。

但是最后,您有一个每个选项卡一个用户的应用程序,(希望)只是按照它应该的方式行事,而不必设置配置文件、使用 IE 或重写 URL。但是,请确保在每个选项卡中显示登录到该选项卡的用户名..。

另一种可行的方法是创建一个唯一的窗口 id,并将该值与会话 id 一起存储在数据库表中。我经常使用的窗口 id 是整数(现在)。如果窗口被刷新、重新加载或提交给自己,则在打开窗口并重新分配给同一窗口时创建此值。窗口值(输入)使用链接保存在本地表中。当需要一个值时,它是基于窗口 id/session id 链接从数据库表中获得的。虽然这种方法需要一个本地数据库,但它实际上是万无一失的。使用数据库表对我来说很容易,但是我看不出为什么本地数组不能工作得一样好。

Name Javascript 属性是唯一可以跨选项卡活动持久化的属性,但是可以保持独立(而不是 URL guff)。

您可以使用 HTML5 SessionStorage (window.sessionStorage)。 然后,每个浏览器选项卡都有自己的 ID。

使用 sessionStorage 存储的数据不会跨浏览器选项卡持久存储, 即使两个选项卡都包含来自同一域名的网页 换句话说,sessionStorage 中的数据不仅仅局限于 调用页的域和目录,但是 与会话 cookie 相比, 它可以将数据从一个标签保存到另一个标签。

我认为您可能需要的是跨选项卡维护导航状态,而不是专门为每个选项卡创建一个会话。这正是 Seam 框架通过其 Conversation 范围/上下文所实现的。它们的实现依赖于这样一个事实,即每个请求都会传播一个会话 ID,并在服务器端创建一个会话的概念,该概念介于会话和请求之间。它允许导航流控制和状态管理。

虽然这主要针对 JSF,但是看一看,看看是否有什么可以从 http://docs.jboss.org/seam/latest/reference/en-US/html_single/#d0e3620获得一些想法的地方

在 javascript 中,如何唯一地标识一个浏览器窗口和另一个浏览器窗口在相同的基于 cookie 的 sessionId 下

本质上使用 window.name。如果未设置,则将其设置为唯一值并使用它。属于同一个会话的选项卡之间会有所不同。

我一直在读这篇文章,因为我想做同样的事情。对于我正在开发的一个应用程序,我也有类似的情况。实际上,测试比实用性更重要。

在阅读了这些答案,尤其是 Michael Borgwardt 给出的答案之后,我意识到需要存在的工作流程:

  1. 如果用户导航到登录屏幕,请检查现有会话。如果存在,则绕过登录屏幕,将其发送到欢迎屏幕。
  2. 如果用户(在我的例子中)导航到注册屏幕,请检查现有的会话。如果存在会话,请让用户知道您将注销该会话。如果他们同意,注销,并开始注册。

这将解决用户在他们的会话中看到“另一个用户”的数据的问题。他们实际上并没有在他们的会话中看到“另一个用户”的数据,他们实际上看到的是他们打开的唯一一个会话中的数据。显然,这会导致一些有趣的数据,因为一些操作会覆盖一些会话数据,而不覆盖其他会话数据,这样您就可以在单个会话中组合数据。

现在,为了解决测试问题。唯一可行的方法是利用 预处理指令来确定是否应该使用无 cookie 会话。看,通过构建特定环境的特定配置,我能够对环境及其用途做出一些假设。这将允许我在技术上有两个用户同时登录,并且测试人员可以在同一个浏览器会话中测试多个场景,而无需从任何一个服务器会话中登出。

然而,这种方法有一些严重的警告。尤其是测试人员正在测试的是将在生产环境中运行的 没有

所以我想我不得不说,这最终是一个 坏主意。

如果尚未设置 timeStamp,则将其存储在 window.sessionStorage 中。 这将为每个选项卡提供唯一的值(即使 URL 相同)

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

Https://developer.mozilla.org/en-us/docs/web/guide/api/dom/storage

希望这个能帮上忙。

老实说... ... 上面的 一切可能是真的,也可能不是真的,但是看起来 方式太复杂了,或者不知道服务器端正在使用哪个选项卡。

有时候我们需要使用 Occam 的剃须刀。

这是 Occam 的方法: (不,我不是 Occam,他死于1347年)

  1. 在加载时为页面分配一个浏览器唯一标识。当且仅当窗口还没有 id (因此使用前缀和检测)

  2. 在您拥有的每个页面上(使用全局文件或其他东西) ,只需将代码放在适当的位置以检测焦点事件和/或 mouseover 事件。(为了便于编写代码,我将在这一部分使用 jquery)

  3. 在 focus (和/或 mouseover)函数中,设置一个包含 window.name 的 cookie

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

成交。

How to differ sessions in browser-tabs?

在浏览器选项卡中区分会话最直接的方法是不允许您的特定域设置 Cookie。这样,您就可以从单独的选项卡中拥有单独的会话。假设您不允许来自这个域名的 cookies: www.xyz.com。你打开 Tab 1,登录并开始浏览。然后打开 Tab 2,您可以作为同一个用户或不同的用户登录; 无论哪种方式,都将有一个独立于 Tab 1的会话。诸如此类。

但是,当然,当您控制了客户端时,这是可能的。否则,这里的人们提出的解决方案应该适用。

我们有这个问题,我们解决它很容易。我的意思是很容易,因为没有编程参与。 我们想要做的是让用户在同一个浏览器窗口中登录多个帐户,而不会冲突会话。

所以解决方案是随机子域。

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

因此,我们要求系统管理员为 * . abc.com 而不是 abc.com 配置 SSL 证书 然后只需要很少的代码更改,每次用户尝试登录时,他都会在一个带有随机子域名号码的选项卡中登录。因此,每个选项卡都可以独立拥有自己的会话。 另外,为了避免任何冲突,我们使用用户 ID 的 hash 或 md5开发了随机数。

你需要做的

1-存储帐户列表的 cookie

2-可选存储一个默认的 cookie

为每个帐户存储3个索引,如 acc1、 acc2

4-在网址中放入代表帐户索引的内容,如果没有,您将选择默认的一个 像 google mail 一样, domain.com/0/some-url > 0表示帐户索引 您可能还需要知道如何使用 urlwrite

5-当选择一个 cookie 时,根据您的 urlpath 表示帐户索引来选择它

问候

SpringSession 支持同一浏览器中的多个会话 查看示例和实现细节 Http://docs.spring.io/spring-session/docs/current/reference/html5/guides/users.html

我看到许多实现都有客户端更改来操作会话 id cookie。但是通常情况下会话 id cookie 应该是 HttpOnly,这样 java 脚本就不能访问,否则它可能会导致会话劫持通过 XSS

注意: 这里的解决方案需要在应用程序设计阶段完成。以后很难设计出这种方法。

使用隐藏字段传递会话标识符

为了实现这一点,每个页面必须包括一个表单:

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


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


</form>

您这边的每个动作,包括导航,POST 表单(适当设置 action)。对于 “不安全”请求,您可以包含另一个参数,比如包含要提交的数据的 JSON 值:

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

由于没有 cookie,每个选项卡都是独立的,不会知道同一个浏览器中的其他会话。

有很多优势,尤其是在安全方面:

  • 不依赖 JavaScript 或 HTML5。
  • CSRF的固有保护。
  • 不依赖饼干,因此可以抵御 狮子狗
  • 不易受 会话固定攻击。
  • 可以防止使用后退按钮,当您希望用户沿着设定的路径通过您的站点时,这是可取的(这意味着可以防止有时可能被无序请求攻击的逻辑错误)。

一些缺点:

  • 可能需要后退按钮功能。
  • 缓存不是很有效,因为每个操作都是 POST。

更多资料请浏览此处

我通过以下方式解决了这个问题:

  • 我已经为窗口分配了一个名称,这个名称与连接资源相同。
  • 加上1,用于删除存储在 cookie 中的附加连接。
  • 我已经创建了一个函数来捕获所有 xmloutput 响应,并将 sid 和 rid 分配给 json 格式的 cookie。我为每个窗口做这个。名称。

这里是密码:

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);
}

我希望能帮到你

如果是因为每个选项卡将在应用程序中运行不同的流,并且混合两个流会导致问题,那么最好“区域化”会话对象,这样每个流将使用会话的不同区域

这个区域可以简单地实现为每个流有不同的前缀,或者会话对象将包含多个映射(每个流一个) ,并且您使用这些映射而不是会话属性,最好的方法是扩展您的会话类并使用它。