来自同一浏览器的 laravel 和多会话
Posted
技术标签:
【中文标题】来自同一浏览器的 laravel 和多会话【英文标题】:laravel and multi-sessions from the same browser 【发布时间】:2016-01-23 13:07:20 【问题描述】:在我们的网络应用程序中,如果我使用单个浏览器,以用户 A 身份登录我们的应用程序,打开另一个选项卡并以用户 B 身份登录 - 用户 A 丢失了他的会话数据。我认为这是由于使用用户代理制作的共享 cookie。有没有办法将其名称与用户名连接起来?以便会话可以在同一台机器上使用同一浏览器的并发登录用户之间共存?
我们使用 Laravel 5。有什么办法可以解决吗?
【问题讨论】:
您能否提供更多详细信息,为什么需要多个用户在不同的选项卡上登录?难道只是为了测试?人们共用一台电脑? 一些用户可以登录他们在我们的应用程序中拥有的多种类型的帐户。有些人选择在相同的浏览器中进行。因此出现了问题。 如果你可以允许一个用户per type最多可以登录一个帐户的限制,那么我认为这将成为一个更容易的问题。 查看我关于仅在 url 中使用会话 ID 的答案。链接代码还锁定到 IP 和浏览器代理以应对安全风险。 我突然意识到我的回答和你的问题不符!您的问题是关于一个浏览器的不同选项卡中的不同用户。我认为这与服务器端无关,因为一个会话不能被多个用户使用。我认为我们可以使用Token
之类的东西,但不是真正的 php seesion 工具来解决这个问题!
【参考方案1】:
Laravel 会话背景
会话
跳过此部分以获得快速简单的解决方案
在 Laravel 中,会话 cookie 是通过 Illuminate\Session\SessionManager
类创建的,即通过 buildSession
方法:
SessionManager::buildSession
protected function buildSession($handler)
if ($this->app['config']['session.encrypt'])
return new EncryptedStore(
$this->app['config']['session.cookie'], $handler, $this->app['encrypter']
);
else
return new Store($this->app['config']['session.cookie'], $handler);
在这个方法中我们可以清楚的看到会话的名字来自于我们的config\session.php
,特别看这一行:
session.php
'cookie' => 'laravel_session', # ~~ ln 121 at time of writing
好的,但这并没有太大帮助,改变它,改变它到处都是,正如在配置中进行的评论所指出的那样。
此处指定的名称将在每次新会话 cookie 时使用 由框架为每个驱动程序创建。
即使我们可以传递一些动态值,例如:
'cookie' => 'laravel_session' . user()->id,
这会产生一个自相矛盾的、时间结束的、宇宙内爆的结果,因为您正在从 user
请求 id
,该 session
可通过 cookie
名称 laravel_session
查找到的 session
访问。 )
让我们离开SessionManager
,它只是session.php
配置。我们可以从上面看到,无论我们如何处理,我们所有的会话信息都将属于单个 laravel_session
键。
守卫
也许 Guard 会提供更多信息。
Guard 是您在应用程序中进行身份验证的关键,也是使 Laravel 能够快速创建应用程序的众多因素之一。
查看的方法是Guard::user()
。
Guard::user()
在一些初始缓存和注销检查之后做的第一件事就是会话检查。
Guard::user()
$id = $this->session->get($this->getName());
所以在这里,Laravel 正在获取与 getName()
的结果匹配的会话值 - 太棒了 - 我们需要做的就是修改 getName()
来返回一个值,让我们来看看那个方法:
Guard::getName()
public function getName()
return 'login_'.md5(get_class($this));
这很简单。 $this
指的是 Guard 类,所以 md5 实际上总是相同的(如果有人知道 md5 背后的“为什么”类名每次都相同,请发表评论)。
有几个地方应该更新,例如getRecallerName
。
因此,您可以从这里扩展核心 Guard
类并拼接到您的 getName 和 getRecallerName 方法中。
您可能希望围绕此包装一些服务提供者,编写一些单元测试,甚至可能覆盖原来的身份验证管理器。
“天哪,这似乎需要很多工作”
“肯定是比利,肯定是”
https://www.youtube.com/watch?v=dTxQ9yhGnAg
看下一部分
“我只需要一个答案”的快速回答
Ollie Read 已经创建了一个解决方案,可在此处找到:
https://github.com/ollieread/multiauth
我鼓励你看一看,尤其是自定义 Guard
类,它使用自定义 getName
方法扩展了核心 Guard
。
【讨论】:
针对您关于 md5 的问题。 md5'ing 相同的值总是会产生相同的哈希值,因为 md5 默认情况下不使用任何盐,这与 bcrypt 不同。 我想我更好奇为什么如果结果相同,为什么首先要 md5 呢?这不是一种安全措施,因为 md5 低于平均水平,是不是因为类名有点被掩盖了? 啊,是的,我误解了你的问题。我想到的唯一另一件事是,您可能希望将某些内容存储在名为“login_guard”的会话变量中,如果您不小心覆盖了框架保留的某些内容,那将非常糟糕。因此,通过 md5'ing 名称,它对开发人员来说更加防错。 谢谢克里斯。我已经检查了您建议的解决方案,但 github 说“此包已停止”。 @Kamlesh 可能是因为答案是 5 岁 :) “快速答案”之上的所有内容仍然适用【参考方案2】:任何主流浏览器都只会为网站存储一个会话 cookie,但网站开发人员可以选择该 cookie 中的内容。您的网站似乎将用户信息存储在会话 cookie 中,然后当另一个选项卡在同一个 cookie 中存储不同的信息时,该信息会被覆盖。
您没有提供有关您的特定网站如何运作的详细信息,但这里有一些解决此问题的一般方法。
1) 为不同的用户使用不同的浏览器。不同的浏览器不会在它们之间共享 cookie。如果您的目标只是用多个用户测试您的网站,那么这就是方法。您还可以使用隐身/私人模式登录单独的用户,因为此模式也不共享 cookie。
2) 不要使用会话 cookie 来存储用户信息。这在大多数网站上都是不可取的,但如果这是一个内部网站或严格控制的环境,您可以通过 URL、POST 数据或请求中的一些其他隐藏标识符传递用户标识。
3) 将所有当前登录用户的数据存储在会话 cookie 中。 根据 Web 框架,可以创建 user
-> cookieData
的映射并查看根据发出请求的用户来选择正确的。这是一种先进的技术,我实际上不知道 Laravel 是否公开了这种级别的控制。
【讨论】:
【参考方案3】:最简单的方法只是基于 URL 的 sessionID,这可能是一个安全问题,具体取决于您的应用程序的设计方式,尤其是在与未过期会话共享 url 时。
由于 L5 不再支持 php 原生会话,您必须使用如下自定义提供程序:
这将在 laravel V5 的 url 中使用 sessionID:
https://github.com/iMi-digital/laravel-transsid
基本上会话是基于 URL 的,因此您只需登录不同的选项卡并获取新的 sessionID,如果需要,该人可以轻松地执行“在新选项卡中打开页面”以拥有同一用户的两个页面好吧。
上面的库将会话锁定到 IP 和用户代理,因此链接共享不会意外泄漏会话。
【讨论】:
【参考方案4】:使用同一浏览器(如 google 添加帐户)进行多用户登录。为此,您需要遵循一些步骤并重写 Laravel 提供的 auth 库,
步骤
备份您的 Auth 文件。
将所有会话存储功能更改为首先将其存储在数组中,然后将该数组存储到会话中
现在您需要创建一个新的会话变量,它将存储当前用户实例 ID,例如 user 0 1 2 ...
现在您需要更改所有函数,您将从会话中获取值,您需要检查会话对象是否为空然后用户注销,否则您需要根据用户实例获取用户数据。
当用户想要从一个帐户切换到另一个帐户时,您需要更改您的实例。
【讨论】:
【参考方案5】:tl;dr: 亚格尼
考虑一个具有 2 个身份的人(在您的情况下为 http 客户端):Dr Jekyll
和 Mr Hyde
。
他拜访了他的新朋友 Sir RM1970
(你的情况是 http 服务器):“你好吗,RM1970
!”。
这是问题所在。可怜的RM1970
要迎接怪物回来了,选择不多:
Dr Jekyll
和 Mr Hyde
!你是怎么做到的!”,这使进一步的对话变得非常复杂(例如,你的 ACl 需要使用身份列表,并在它们发生冲突时实时决定优先级)
自己做出决定:“你好吗Dr Jekyll
!”并祈祷你做出了正确的选择(随机选择用户的身份,并通过不可预知的反应给你的用户带来一些乐趣)
狡猾地把这个责任转给他:“原谅我?你是谁?说出你自己的名字!”(每个请求都需要单一身份)
后者是它的实际工作方式。浏览器提供最新确认的身份。 你被要求改变这个,但你真的想要吗?坚持到底,不要承担这个责任。 如果您不使用前 2 个死胡同选项,则需要询问用户他代表哪个发送请求。这里最好的选择是让你的前端有状态,维护打开的会话列表,并提供一个 UI 供用户选择。这几乎是 Ryan Bemrose 的第三个选项,但是将这些数据存储在客户端,并且只发送选择的那个。无需更改 laravel 后端。
这里的问题是切换选项卡不会自动切换用户,而且会相当混乱,与已经实现的注销/登录路径几乎没有区别。
某些浏览器支持多个配置文件 (example),这可能是一个可接受的替代方案。基本上它与 1st Ryan Bemrose 的选项相同,但不需要安装多个浏览器,并且可以受益于永久 cookie,即“记住我”。
【讨论】:
【参考方案6】:我不完全知道您需要这个做什么,但作为开发人员,我有时必须登录到具有多个用户的应用程序。为此,我通常使用隐身模式,或者如果它的用户超过 2 个,我在 chrome 中使用 this 扩展名时运气不错。
我知道这不是您问题的答案,但它可能正是您想要的。
【讨论】:
正在寻找更程序化的解决方案。我无法控制用户的环境,也不能强迫他们使用 chrome 或它的某些扩展。 我对您的问题进行了更多研究,this 描述了一些我可以看到的工作。您必须在会话中添加选项卡 ID 并覆盖 laravel 身份验证以提取与给定选项卡关联的用户。相当多的工作和测试要写,但我可以看到它工作。当然也有一些缺点。您必须登录到每个新选项卡(可能能够使用更多变通方法),并且有人提到了重复的选项卡。希望有帮助!【参考方案7】:同时登录的用户之间存在不同的视图不能仅仅通过会话cookie来实现,因为cookie是由浏览器存储的。所以记录的
在用户看来,必须由服务器存储。
众所周知,一旦调用session_start
,就会创建SessionID,然后在服务器的临时文件中创建临时文件
目录。
不同的用户有不同的SessionID,在调用session_destory
之后,所有存储在Server和Cookies中的SessionID都被恢复了。您可以通过实现 SessionHandlerInterface 来重写此行为。当然很多 web 框架都支持这个,Laravel 也不例外。
这是文件:
custom-session-drivers
【讨论】:
【参考方案8】:我不知道将其编码到 laravel 中有多复杂,但这可能是一种解决方案:
您使用不同的会话名称,必须是一个字符串,并且每次都将其编码到 url 中,以便应用程序知道哪个用户发出了请求。所以你可以用一个普通的名字来调用会话变量。
<?php
if(isset($_GET['id']) && !empty($_GET['id']))
session_name($_GET['id']);
session_start();
if(isset($_GET['user']))
$_SESSION['user'] = $_GET['user'];
if(!empty($_SESSION['user']))
echo "Hello ".$_SESSION['user'];
【讨论】:
Laravel 不使用原生 php 会话,它有自己的实现。以上是关于来自同一浏览器的 laravel 和多会话的主要内容,如果未能解决你的问题,请参考以下文章