跳至主要内容
版本:5.1

登录还是不登录

开发者须知

不建议创建直接包含加载并启动 CMS 框架的脚本,并且仅在极少数情况下需要这样做。根据用例,您应该创建一个控制台插件并扩展 Joomla! 控制台应用程序。如果您需要为网络请求创建一个简单的入口点,请编写一个Ajax 插件

登录还是不登录

正如您从上一节中的脚本中看到的,无需登录 Joomla 即可对数据库执行查询并输出结果。但是您是否应该登录 Joomla?您有 3 个选项

  • 不登录
  • 使用硬编码到脚本中的凭据登录
  • 检查用户是否已登录。

让我们按相反的顺序考虑这些选项。

检查用户是否已登录

Joomla 通过使用 cookie 来判断用户是否已登录,如图表所示。

当 Joomla 响应 HTTP 请求时,它会将 cookie 发送到浏览器,其中包括一个会话 cookie,它类似于指向存储在服务器上的会话数据的指针。如果用户登录 Joomla 站点,则该会话数据将包含他们登录时使用的用户名。

当用户下次查看站点上的页面或提交站点上的表单时,浏览器会将 Joomla 站点之前发送的所有 cookie 发送到 Joomla 服务器。Joomla 找到会话 cookie,并通过它找到会话数据,并在其中检查是否存在已登录的用户,如果存在,则检查关联的用户名是什么。

当用户访问 Joomla 站点时,他们实际上是在访问 Joomla 最高层目录中的 index.php 脚本。但是,浏览器会为同一域中的所有脚本发送这些 cookie。因此,如果用户导航到<your domain>/cli/myscript.php,则浏览器将在 HTTP 请求中发送存储在该域的所有 cookie。

因此,在您的脚本中,您可以让 Joomla 找到会话 cookie,并通过它找到会话数据,并可以找出已登录用户的用户名。为此,您通常使用

$user = \Joomla\CMS\Factory::getApplication()->getSession()->get('user');

但在这种情况下,已经有一个变量$app指向 Application 实例,因此您可以使用

$user = $app->getSession()->get('user');

您将获得一个Joomla\CMS\User\User对象,其中

  • 如果用户未登录,则 id 设置为 0,或者
  • 已登录用户的 id > 0。

您可以在User API中找到此 User 对象的其他属性。

如果您的脚本使用其他 Joomla 库或扩展代码,则该代码可能会尝试通过以下方式访问 User 对象:

$app->getIdentity();

因此,要使此功能正常工作,您需要调用

$app->loadIdentity($user);

以便将指向 User 实例的指针存储在 Application 实例中。

会话 cookie 仅在可配置的时间段内有效(在全局配置/系统选项卡、会话生存时间中设置),如果会话 cookie 已过期,则它不会导致会话数据产生任何已登录的用户。会话生存时间通常很短,以降低其他人稍后使用同一设备并获得 Joomla 站点登录访问权限的风险。

如果用户是设备的唯一用户,则当他们登录时,可以选中“记住我”复选框。这会导致 Joomla 发出一个“记住我”cookie,它的持续时间比会话 cookie 长得多。如果用户随后访问 Joomla 站点,即使会话 cookie 已过期,“记住我”cookie 也会导致用户被视为已登录到站点。

在 Joomla 中,“记住我”cookie 是使用系统插件(在 plugins/system/remember 中)实现的,该插件侦听 onAfterInitialisation 事件。如果用户是“访客”并且未登录,则插件会查找“记住我”cookie 的是否存在,如果找到,则找到关联的用户并将其视为已登录(实际上由身份验证 cookie 插件执行)。

但是,“记住我”cookie 的实际名称包含发出它的 php 脚本的目录名称,默认情况下是包含站点 index.php 文件的顶级目录。因此,要使用“记住我”cookie,您必须将脚本放在与站点 index.php 文件相同的顶级目录中。

要使用“记住我”cookie,您只需触发onAfterInitialise事件即可。然后,您可以检查会话是否现在具有用户详细信息。以下是完整代码

// Instantiate the application.
$app = $container->get(\Joomla\CMS\Application\SiteApplication::class);
// Set the application as global app
\Joomla\CMS\Factory::$application = $app;
$user = $app->getSession()->get('user');
if ($user->id > 0){
echo "<h3>logged on as {$user->name}</h3>";
} else {
echo "<h3>not logged on</h3>";
}

// Trigger the onAfterInitialise event after ensuring that system plugins are loaded
\Joomla\CMS\Plugin\PluginHelper::importPlugin('system');
$app->triggerEvent('onAfterInitialise');

if ($user->id == 0) {
// check if the remember me plugin has now logged the user in
$user = $app->getSession()->get('user');
if ($user->id > 0) {
echo "<h3>logged on as {$user->name} via remember me cookie</h3>";
} else {
echo "<h3>still not logged on</h3>";
}
}

重申一下,您必须将自定义 PHP 脚本放在与站点 index.php 文件相同的顶级文件夹中,并且当然必须启用“记住我”和“身份验证 - Cookie”插件。

(顺便说一句,如果您使用非常短的会话生存时间进行测试,并且想知道为什么会话 cookie 没有过期,可能是因为 Joomla 集成了“保持活动”功能,其中涉及 javascript 代码向服务器发送 HTTP“保持活动”HTTP 请求,这会使会话 cookie 保持更新状态并且不会过期。)

使用硬编码凭据登录

您可以通过以下方式执行此操作:

$username = "myuser";
$password = "mypassword";
$credentials = ['username' => $username, 'password' => $password];
$logged_in = $app->login($credentials, []);

正如您从上一节中的基本脚本中看到的,您无需登录即可执行数据库查询等操作。但是,如果您使用 Joomla 代码(例如com_content ArticleModel),则您可能会发现该代码会在允许操作之前检查权限,因此您需要以授权用户的身份登录才能使该功能正常工作。

当然,如果您使用硬编码凭据,则不会检查运行脚本的人员是否是 Joomla 站点的有效用户,因此功能必须对任何人都开放,或者您必须定义其他机制来限制对 PHP 脚本的访问。

这种方法的另一个主要危险是,您可能会让某人使用您的硬编码凭据登录到您的 Joomla 站点。某人可以运行您的 PHP 脚本,并在之后立即访问 Joomla 站点,发现自己以您在脚本中硬编码的用户名登录。这是因为 Joomla 启动例程设置了 PHP 注册关闭函数,这些函数在脚本完成时接收控制权。这些例程随后存储包含您的硬编码用户名的会话数据,并且对脚本的 HTTP 响应包括发送到浏览器的会话 cookie。如果用户随后访问 Joomla 站点,浏览器会将会话 cookie 发送到服务器,Joomla 读取它并访问会话数据,并将找到的任何用户视为已登录。

因此,建议您在脚本中专门注销用户

$app->logout();

即使这样,如果正在运行的代码导致异常,并且您没有将其包含在 try/catch 块中,则通用 Joomla 异常处理程序将获得控制权,这将导致您的脚本在您的logout()调用之前突然终止,并存储会话数据中的用户详细信息。

总结

在决定选择哪个登录选项时,您可能需要考虑以下一些因素

  • 是否允许绝对任何人运行此脚本?
  • 是否通过其他方式充分限制对脚本的访问?
  • 脚本中运行的 Joomla 功能是否需要已登录的用户才能正常工作?
  • 如果我确实硬编码了凭据,是否有万无一失的机制来确保运行脚本的用户不会意外使用这些凭据登录?