在我们的网络应用程序中,如果我使用单个浏览器,以用户 A 身份登录我们的应用程序,打开另一个选项卡并以用户 B 身份登录 - 用户 A 丢失了他的会话数据。我认为这是由于与用户代理共享的cookie。有没有办法将其名称与用户名连接起来?以便会话可以在同一台计算机上使用同一浏览器的并发登录用户之间共存?
我们使用Laravel 5。有什么办法吗?
拉拉维尔会议背景
会话
跳过此部分以获得快速简便的解决方案
在 Laravel 中,会话 cookie 是通过 Illuminate'Session'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
,特别是看这一行:
会话.php
'cookie' => 'laravel_session', # ~~ ln 121 at time of writing
好的,但这并没有多大帮助,更改它,到处更改它,正如在配置中进行它的注释所指出的那样。
每次使用新的会话 cookie 时都会使用此处指定的名称 由每个驱动程序的框架创建。
即使我们可以传递给它一些动态值,例如:
'cookie' => 'laravel_session' . user()->id,
这会产生一个矛盾的、时间结束的、宇宙内爆的结果,因为你正在从user
请求id
,该是通过cookie
名称laravel_session
查找的session
访问的。(脑洞大开(
让我们不SessionManager
,它单独session.php
配置。我们可以从上面看到,无论我们如何处理,我们所有的会话信息都将属于该laravel_session
键。
警卫
也许卫兵会有更多的信息。
Guard 是您身份验证应用程序的关键,也是使 Laravel成为快速创建应用程序的众多原因之一。
查看方法是Guard::user()
。
在进行一些初始缓存和注销检查后,Guard::user()
做的第一件事就是会话检查。
守卫::用户((
$id = $this->session->get($this->getName());
所以在这里,Laravel正在获取与getName()
结果匹配的会话值 - 太棒了 - 我们需要做的就是mod getName()
返回一个值,让我们来看看该方法:
守卫::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
。
任何主要的浏览器只会为一个网站存储一个会话cookie,但网站开发人员可以选择该cookie中的内容。 您的网站似乎将用户信息存储在会话 Cookie 中,然后当另一个选项卡在同一 Cookie 中存储不同的信息时,该信息会被覆盖。
您没有提供有关特定网站如何运行的详细信息,但这里有一些解决此问题的一般方法。
1(为不同的用户使用不同的浏览器。 不同的浏览器不会在它们之间共享 Cookie。 如果您的目标只是与多个用户一起测试您的网站,那么这就是方法。 您还可以使用隐身/私人模式登录单独的用户,因为此模式也不会共享 Cookie。
2( 请勿使用会话 Cookie 来存储用户信息。 这在大多数网站上是不可能的,但如果这是一个内部网站或严格控制的环境,您可以通过 URL、POST 数据或请求中的其他一些隐藏标识符传递用户标识。
3( 在会话 Cookie 中存储所有当前登录用户的数据。 根据 Web 框架,可以创建 user
-> cookieData
的映射,并根据发出请求的用户查找正确的映射。 这是一种先进的技术,我实际上不知道Laravel是否暴露了这种级别的控制。
多用户登录与谷歌添加帐户相同的浏览器。 为此,您需要遵循一些步骤并重写由 Laravel 提供的身份验证库,
步骤
固定备份您的身份验证文件。
更改所有会话存储功能以首先将其存储在数组中,然后将该阵列存储到会话
现在您需要创建新的会话变量,该变量将存储当前用户实例 ID,如用户 0 1 2 ...
现在您需要更改所有函数,您将从会话中获取值,您需要检查会话对象是否为空,然后用户注销,否则您需要在用户实例上获取用户的数据。
当用户想要从一个账户切换到另一个账户时,您需要更改实例。
最简单的是基于 URL 的会话 ID,这可能是一个安全问题,具体取决于应用程序的设计方式,尤其是在与未过期会话共享 URL 时。
由于 L5 不再支持 php 本机会话,因此您必须使用如下所示的自定义提供程序:
这将在 laravel V5 的 url 中使用 sessionID:
https://github.com/iMi-digital/laravel-transsid
基本上,会话是基于URL的,因此您只需在不同的选项卡中登录并获得新的sessionID,如果需要,该人可以轻松地执行"在新选项卡中打开页面"以拥有同一用户的两个页面。
上面的库将会话锁定到 IP 和用户代理,因此链接共享不会意外泄漏会话。
tl;dr: Yagni
考虑一个人(在你的例子中是http客户端(有2个身份:Dr Jekyll
和Mr Hyde
.
他拜访了他的新朋友RM1970
爵士(在你的例子中是http服务器(:">你怎么做,RM1970
!".
这就是问题所在。可怜的RM1970
需要欢迎怪物回来,而且选择很少:
- 深深地陷入这个兔子洞:">你怎么
Dr Jekyll
和Mr Hyde
都做!",这使进一步的对话变得非常复杂(例如,您的 ACl 需要使用标识列表进行操作,并在它们实时冲突时做出优先级决策( - 自己做决定:">你怎么做
Dr Jekyll
!"并祈祷你做出了正确的选择(随机选择用户的身份,并通过不可预测的响应给你的用户带来一些乐趣( - 狡猾地把这个责任推回给他:">原谅我?你是谁?说出你自己的名字!"(每个请求需要单个标识(
后者是它的实际工作方式。浏览器提供最新的已确认身份.
你被要求改变这一点,但你真的想要它吗?坚守底线,不要承担这个责任.
如果您不打算使用前 2 个死胡同选项,则需要询问用户代表哪个代表发送请求。此处的最佳选择是使前端有状态,维护打开的会话列表,并提供一个 UI 供用户选择一个。这几乎是 Ryan Bemrose 的第三个选项,但将此数据存储在客户端,并仅发送所选的那个。无需更改 laravel 后端。
这里的问题是切换选项卡不会自动切换用户,并且会相当混乱,与已经实现的注销/登录路径几乎没有区别。
某些浏览器支持多个配置文件(示例(,这可能是可接受的替代方案。基本上,它与1st Ryan Bemrose的选项相同,但不需要安装多个浏览器,并且可以从永久cookie中受益,又名"记住我"。
我不完全知道你需要这个做什么,但作为开发人员,我有时必须登录具有多个用户的应用程序。为此,我通常使用隐身模式,或者如果它超过 2 个用户,我在 chrome 中使用此扩展程序有一些运气。
我知道这不是您问题的答案,但它可能是您想要的。
并发登录用户之间的不同视图共存,不能仅由会话cookie实现,因为cookie是由浏览器存储的。所以记录的在用户看来,必须由服务器存储。众所周知,一旦调用session_start
,就会创建SessionID,然后在服务器的临时中创建临时文件目录。
用户具有不同的会话ID,调用session_destory
后,存储在服务器和Cookie中的所有会话ID都将恢复。您可以通过实现 SessionHandlerInterface(会话处理程序接口(来重写此行为。由于许多Web框架支持这一点,Laravel也不例外。这是文档:自定义会话驱动程序
将其编码到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'];