首页 文章

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

提问于
浏览
123

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

谢谢 .

21 回答

  • 0

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

  • 19

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

    虽然你可以从中获取一些想法:http://docs.jboss.org/seam/latest/reference/en-US/html_single/#d0e3620

  • 1

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

    使用sessionStorage存储的数据不会在浏览器选项卡中保留,即使两个选项卡都包含来自同一域源的网页也是如此 . 换句话说,sessionStorage中的数据不仅限于调用页面的域和目录,而且还包含页面所在的浏览器选项卡 . 与会话cookie相对照,会话cookie将数据从选项卡保留到选项卡 .

  • 0

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

    • Cookies . 更干净,更流行的方法,但它意味着一个用户的所有浏览器标签和窗口共享会话 - IMO这实际上是可取的,我会非常恼火的一个网站让我登录每个新标签,因为我非常密集地使用标签

    • URL重写 . 网站上的任何URL都附加了会话ID . 这是更多的工作(你必须在任何地方做一些你有站点内部链接的东西),但是可以在不同的选项卡中有单独的会话,虽然通过链接打开的选项卡仍然会共享会话 . 这也意味着用户在访问您的网站时始终必须登录 .

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

    Edit: 对于测试,可以找到其他解决方案(例如在不同的VM上运行多个浏览器实例) . 如果一个用户需要同时以不同的角色行事,那么应该在应用程序中处理"role"概念,以便一个登录可以有多个角色 . 您根本无法使用基于cookie的会话单独处理浏览器选项卡 .

  • 2

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

  • 0

    You shouldn't. 如果你想做这样的事情要么你需要强制用户通过动态编写URL来使用你的应用程序的单个实例,使用sessionID(不是sessionid它将无法工作)id并在每个URL中传递它 .

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

  • 2

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

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

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

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

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

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

  • 0

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

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

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

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

  • 13

    我会在这里说实话 . . . 上面的 everything 可能是也可能不是,但这似乎太复杂了,或者没有解决知道服务器端使用什么选项卡的问题 .

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

    Here's the Occam's approach: (不,我不是奥卡姆,他于1347年去世)

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

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

    3)在你的焦点(和/或鼠标悬停)功能中,设置一个带有window.name的cookie

    4)当您需要读取/写入选项卡特定数据时,从服务器端读取该cookie值 .

    Client side:

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

    server side (C# - for example purposes)

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

    完成 .

  • 0

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

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

  • 0

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

  • 16

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

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

  • 9

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

  • 83

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

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

    • 如果用户导航到登录屏幕,请检查现有会话 . 如果存在,则绕过登录屏幕并将其发送到欢迎屏幕 .

    • 如果用户(在我的情况下)导航到注册屏幕,请检查现有会话 . 如果存在,请让用户知道您将记录该会话 . 如果他们同意,请退出并开始注册 .

    这将解决用户在其会话中看到“另一个用户”数据的问题 . 他们在会话中并没有真正看到“另一个用户”的数据,他们真的看到了他们打开的唯一会话中的数据 . 显然,这会导致一些有趣的数据,因为某些操作会覆盖某些会话数据而不会覆盖其他会话数据,因此您在该单个会话中拥有数据组合 .

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

    但是,这种方法有一些严重的警告 . 其中最重要的是,测试人员正在测试的是 not 将在 生产环境 中运行什么 .

    所以我想我必须说,这最终是 bad idea.

  • 1

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

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

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

    希望这可以帮助 .

  • 1
    How to differ sessions in browser-tabs?
    

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

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

  • 0

    你需要做的

    1-存储帐户列表的cookie

    2-可选存储默认cookie的cookie

    每个帐户有3个商店,其索引类似于acc1,acc2

    4-在url中放置一些代表帐户的索引,如果不是你将选择默认的一个像google mail domain.com/0/some-url >> 0这里代表帐户的索引你也可能需要知道如何使用urlwrite

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

    问候

  • 1

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

  • 14

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

    Use a hidden field to pass around the session identifier .

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

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

  • 0

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

    • 我已经为窗口分配了一个名称,这个名称与连接资源相同 .

    • 加1来摆脱存储在cookie中的附加连接 .

    • 我创建了一个捕获所有xmloutput响应的函数,并以json格式将sid和rid分配给cookie . 我为每个window.name执行此操作 .

    这里的代码:

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

    我希望能帮助你

  • 4

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

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

相关问题