如何在浏览器标签中区分会话?

时间:2008-12-15 15:10:58

标签: jsp servlets browser web-applications sessionid

在使用JSP和Servlets在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),然后打开浏览器(FF,IE7或Opera)使用正确的网址(localhost/context1/testpage.jsp),在输入中输入您的姓名并提交表单。然后在同一浏览器中打开一个新选项卡,然后您可以在新选项卡上看到您的名称(从会话中获取)。小心浏览器缓存,有时似乎不会发生,但它在缓存中,刷新第二个选项卡。

感谢。

21 个答案:

答案 0 :(得分:86)

您可以使用HTML5 SessionStorage(window.sessionStorage)。您将生成随机ID并保存在会话存储每个浏览器选项卡中。 然后每个浏览器选项卡都有自己的ID。

  

使用sessionStorage存储的数据不会跨浏览器标签保留,   即使两个标签都包含来自同一域来源的网页。在   换句话说,sessionStorage中的数据不仅限于   调用页面的域和目录,但是浏览器选项卡中   页面包含在哪里。与会话cookie对比,   它会将数据从选项卡保留到选项卡。

答案 1 :(得分:22)

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

  • 缓存数据。更干净,更流行的方法,但它意味着一个用户的所有浏览器标签和窗口共享会话 - IMO这实际上是可取的,我会非常恼火的一个网站让我登录每个新标签,因为我非常密集地使用标签
  • 网址重写。网站上的任何URL都附加了会话ID。这是更多的工作(你必须在任何地方做一些你有站点内部链接的东西),但是可以在不同的选项卡中有单独的会话,虽然通过链接打开的选项卡仍然会共享会话。这也意味着用户在访问您的网站时始终必须登录。

你还想做什么?为什么要让标签有单独的会话?也许有一种方法可以在不使用会话的情况下实现目标?

修改 对于测试,可以找到其他解决方案(例如在不同的VM上运行多个浏览器实例)。如果一个用户需要同时以不同的角色行事,那么应该在应用程序中处理“角色”概念,以便一个登录可以有多个角色。你必须决定是否使用URL重写,或者仅仅使用当前情况更容易接受,因为根本不可能使用基于cookie的会话单独处理浏览器选项卡。

答案 2 :(得分:16)

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

我不知道你为什么需要它,但除非你需要制作一个完全无法使用的应用程序,否则不要这样做。

答案 3 :(得分:16)

window.name Javascript属性是唯一可以在制表符活动中保留的东西,但可以保持独立(而不是URL guff)。

答案 4 :(得分:13)

我想出了一个新的解决方案,它有一点点开销,但似乎是原型的工作。一个假设是您在荣誉系统环境中登录,尽管可以通过在切换标签时重新请求密码来进行调整。

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

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

另一个是竞争条件:如果你可以足够快地切换窗口以使其混淆,你最终可能会得到一个relogin1-&gt; relogin2-&gt; ajax1-&gt; ajax2序列,其中ajax1是在错误的会话下进行的。通过将登录AJAX请求推送到阵列,然后在存储上并在发出新的登录请求之前中止所有当前请求来解决此问题。

最后要注意的是窗口刷新。如果有人在您的AJAX登录请求处于活动状态但尚未完成时刷新窗口,则会以错误的人的名义刷新。在这种情况下,您可以使用非标准的beforeunload事件来警告用户潜在的混淆并要求他们单击取消,同时重新发出AJAX登录请求。然后他们可以解决它的唯一方法是在请求完成之前单击“确定”(或者不小心点击输入/空格键,因为OK - 不幸的是在这种情况下 - 是默认值。)还有其他方法来处理这种情况,比如检测F5和Ctrl + R / Alt + R按下,这在大多数情况下都有效,但可能会被用户键盘快捷键重新配置或替代操作系统使用所阻碍。然而,这实际上是一个边缘情况,最糟糕的情况永远不会那么糟糕:在荣誉系统配置中,你将以错误的人身份登录(但你可以明确表示这是案例通过个性化页面的颜色,样式,突出显示的名称等);在密码配置中,最后一个人输入他们的密码以登出或共享他们的会话,或者如果这个人实际上是当前用户,那么就没有违规行为。

但最终你有一个每用户一个用户的应用程序(希望)只是按照应有的方式运行,而不必设置配置文件,使用IE或重写URL。确保在登录到特定选项卡的每个选项卡中明确显示,但是......

答案 5 :(得分:10)

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

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

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

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

答案 6 :(得分:4)

我在这里说实话。 。上面的一切可能是也可能不是,但似乎 WAY 过于复杂,或者无法解决知道哪些标签正在服务器端使用。

有时我们需要使用奥卡姆剃刀。

以下是奥卡姆的做法:(不,我不是奥卡姆,他于1347年去世)

1)在加载时为您的页面分配浏览器唯一ID。 。 。当且仅当窗口还没有id时(所以使用前缀和检测)

2)在您拥有的每个页面上(使用全局文件或其他内容)只需将代码放置到位以检测焦点事件和/或鼠标悬停事件。 (为了便于编写代码,我将在这部分使用jquery)

3)在你的焦点(和/或鼠标悬停)功能中,设置一个带有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];

完成。

答案 7 :(得分:2)

我认为你可能想要的是保持标签之间的导航状态,而不是专门为每个标签创建一个会话。这正是Seam框架通过其Conversation范围/上下文实现的目标。它们的实现依赖于以下事实:会话ID随每个请求一起传播,并在服务器端创建会话的概念,这是会话和请求之间的事物。它允许导航流量控制和状态管理。

虽然这主要是针对JSF,但请查看是否可以从中获取一些想法:http://docs.jboss.org/seam/latest/reference/en-US/html_single/#d0e3620

答案 8 :(得分:2)

In javascript, how can I uniquely identify one browser window from another which are under the same cookiedbased sessionId

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

答案 9 :(得分:1)

您可以使用链接重写在单个页面开始时为所有网址添加唯一标识符(例如index.html / jsp / whatever)。浏览器将对您的所有标签使用相同的Cookie,因此您在Cookie中放置的所有内容都是唯一的。

答案 10 :(得分:1)

我最近使用Cookie开发了这个问题的解决方案。 这是我的解决方案的链接。我还使用ASP.NET包含了解决方案的示例代码,如果需要,您应该能够将其调整为JSP或Servelets。

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

答案 11 :(得分:1)

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

答案 12 :(得分:1)

Spring Session支持在同一浏览器中进行多个会话 查看示例和实现细节 http://docs.spring.io/spring-session/docs/current/reference/html5/guides/users.html

答案 13 :(得分:0)

如果因为每个标签在您的应用程序中运行不同的流程,并且混合两个流程都会导致问题,那么区域化&#34;区域化&#34;您的会话对象,以便每个流将使用会话的不同区域

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

答案 14 :(得分:0)

I resolved this of following way:

  • I've assigned a name to window this name is the same of connection resource.
  • plus 1 to rid stored in cookie for attach connection.
  • I've created a function to capture all xmloutput response and assign sid and rid to cookie in json format. I do this for each window.name.

here the code:

net.sf.jasperreports.engine.JRException: net.sf.jasperreports.engine.JRException: Unable to get next record from result set.
at com.jaspersoft.studio.editor.preview.view.control.ReportControler.fillReport(ReportControler.java:524)
at com.jaspersoft.studio.editor.preview.view.control.ReportControler.access$20(ReportControler.java:499)
at com.jaspersoft.studio.editor.preview.view.control.ReportControler$5.run(ReportControler.java:380)
at org.eclipse.core.internal.jobs.Worker.run(Worker.java:55)
Caused by: net.sf.jasperreports.engine.JRException: Unable to get next record from result set.
at net.sf.jasperreports.engine.JRResultSetDataSource.next(JRResultSetDataSource.java:134)
at net.sf.jasperreports.engine.fill.JRFillDataset.advanceDataSource(JRFillDataset.java:1422)
at net.sf.jasperreports.engine.fill.JRFillDataset.next(JRFillDataset.java:1271)
at net.sf.jasperreports.engine.fill.JRFillDataset.next(JRFillDataset.java:1250)
at net.sf.jasperreports.engine.fill.JRBaseFiller.next(JRBaseFiller.java:1010)
at net.sf.jasperreports.engine.fill.JRVerticalFiller.fillReport(JRVerticalFiller.java:121)
at net.sf.jasperreports.engine.fill.JRBaseFiller.fill(JRBaseFiller.java:558)
at net.sf.jasperreports.engine.fill.BaseFillHandle$ReportFill.run(BaseFillHandle.java:119)
at java.lang.Thread.run(Thread.java:745)
Caused by: org.firebirdsql.jdbc.FBSQLException: The result set is closed
at org.firebirdsql.jdbc.AbstractResultSet.checkOpen(AbstractResultSet.java:233)
at org.firebirdsql.jdbc.AbstractResultSet.checkCursorMove(AbstractResultSet.java:222)
at org.firebirdsql.jdbc.AbstractResultSet.next(AbstractResultSet.java:284)
at net.sf.jasperreports.engine.JRResultSetDataSource.next(JRResultSetDataSource.java:130)
... 8 more

I hope help you

答案 15 :(得分:0)

注意:此处的解决方案需要在应用程序设计阶段完成。以后很难设计这个。

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

为此,每个页面必须包含一个表单:

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

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

</form>

您身边的每一个操作,包括导航,都会将表格张贴回来(根据需要设置action)。对于"unsafe"个请求,您可以包含另一个参数,例如包含要提交的数据的JSON值:

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

由于没有cookie,每个标签都是独立的,不会在同一浏览器中了解其他会话。

许多优点,特别是在安全性方面:

  • 不依赖JavaScript或HTML5。
  • 固有地防范CSRF
  • 不依赖Cookie,因此可以防范POODLE
  • 不容易受到session fixation
  • 的攻击
  • 可以防止后退按钮的使用,这是您希望用户在您的站点中遵循设置路径时所希望的(这可能会阻止有时可能被无序请求攻击的逻辑错误)。

一些缺点:

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

Further information here

答案 16 :(得分:0)

我看到很多实现都有客户端更改来操作会话ID cookie。但是在一般会话中,id cookie应该是HttpOnly所以java脚本无法访问,否则可能导致会话劫持通过XSS

答案 17 :(得分:0)

你需要做

1-存储帐户列表的cookie

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

每个帐户的3个商店及其索引,如acc1,acc2

4-在url中输入代表帐户索引的内容,如果不是,则选择默认帐户 像google mail domain.com/0/some-url&gt;&gt; 0这里代表帐户的索引 您也可能需要知道如何使用urlwrite

5-选择cookie时,根据你的urlpath选择它代表帐号索引

此致

答案 18 :(得分:0)

How to differ sessions in browser-tabs?

在浏览器标签中区分会话的最简单方法是禁止您的特定域设置Cookie。这样,您可以从单独的选项卡中分别创建会话。假设您禁止来自此域名的Cookie:www.xyz.com。您打开选项卡1,登录并开始浏览。然后打开Tab 2,您可以以同一用户或不同用户身份登录;无论哪种方式,你都会有一个与标签1分开的会话。依此类推。

但是当你掌控客户端时,这当然是可能的。否则,这里人们规定的解决方案应该适用。

答案 19 :(得分:0)

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

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

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

希望这有帮助。

答案 20 :(得分:0)

我一直在读这篇文章因为我以为我想做同样的事情。对于我正在处理的应用程序,我有类似的情况。实际上,这不仅仅是测试而是实用性。

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

  1. 如果用户导航到登录屏幕,请检查现有会话。如果存在,则绕过登录屏幕并将其发送到欢迎屏幕。
  2. 如果用户(在我的情况下)导航到注册屏幕,请检查现有会话。如果存在,请让用户知道您要记录该会话。如果他们同意,请退出并开始注册。
  3. 这将解决用户在其会话中看到“其他用户”数据的问题。他们在会话中并没有真正看到“另一个用户”的数据,他们真的看到了他们打开的唯一会话中的数据。很明显,这会产生一些有趣的数据,因为有些操作会覆盖某些会话数据而不会覆盖其他会话数据,因此您在该单个会话中拥有数据组合。

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

    然而,这种方法有一些严重的警告。其中最重要的是,测试人员正在测试的是将在生产中运行什么。

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