2

我想知道是否最好检查用户是否使用他的会话登录到网站而不是查询我的数据库。

如果他的ID存在于数据库中,那么为每个用户(想想数千个用户)在每个子页面上查询数据库是不是有点太多了?

在某些条件下仅检查存储在用户会话中的值不是更好、更快且不是那么数据库繁重(例如 ID 必须至少为 10 位长,姓氏必须存在,并且字符串是应该存在网站运营商刚刚知道的)。难道这不能被视为一种(99%)检查用户是否合法登录的安全方法吗?我读过 PHP 以编码方式保存会话内容,因此对于想要获取内容以模拟另一个用户的人来说,它是不可读的。

4

6 回答 6

4

我相信你的问题存在误解。会话和数据库扮演着不同的角色,尽管它们都是数据存储。会话是临时存储,而数据库是永久存储。数据保留在数据库中,直到您明确删除它,但会话带有到期日期。数据库和会话之间还有另一个主要区别,称为会话 ID。Session-id 是一种将 HTTP 请求与服务器上的适当会话数据相关联的方法。会话 ID 通常作为 cookie 在 Web 服务器和浏览器之间来回传输。这是会话如何工作的典型场景:

  1. 浏览器的第一个请求已到达 Web 服务器。服务器上的软件处理传入的请求并看到它不包含会话 ID(因为它是第一个请求)。因此,会为此请求创建一个随机生成的唯一会话 ID,并与响应一起发送回客户端(无论它可能是什么)。在服务器上还创建了一个与新创建的会话 ID 相关联的存储。

  2. 用户请求同一服务器上的另一个页面。这一次,当请求到达服务器时,它带有一个 session-id,因此与其为该请求创建新的 session-id 不同,而是加载与其关联的数据。如果服务器上的软件更改了数据,则在将响应发送回浏览器时将其存储回存储器。

  3. 从现在开始,发送到 Web 服务器的每个请求都会加载相同的会话数据。除非从服务器中删除会话数据或会话 ID,否则此过程将继续。

在所解释的场景中,会话用于保持与请求相关联的数据。会话数据的主要功能之一是存储用户的凭证数据。这是另一种情况:

  1. 用户打开网站的第一页。为他创建了一个 session-id 并将其发送回他的浏览器。

  2. 用户进入登录页面并填写表格并按下提交按钮。

  3. 登录请求已到达服务器。用户名和密码相互检查,如果验证,则在会话数据中提到该会话 ID 属于哪个用户。

  4. 从现在开始,服务器上到达的每个请求都会加载包含该请求来自谁的会话数据,并且与数据库无关(除非您将数据库用作会话数据存储)。

这种后期场景称为authentication,这意味着验证请求是否来自他们声称来自谁。在一般情况下,一旦用户通过身份验证,就无需再次对其进行身份验证(除非会话被破坏)。就身份验证而言,唯一必须使用数据库的部分是当您要检查用户名和密码时。

此外,还有另一种情况称为authorization. 在这种情况下,您知道谁在问什么,唯一剩下的就是检查他是否被允许这样做。您知道谁在询问,因为您在会话数据中拥有经过验证的凭据,其中加载了传入请求的会话 ID。授权可以分为两种类型。首先,您可以检查是否允许用户执行请求的操作。其次,您可能想进一步检查并查看是否允许用户对请求的数据执行请求的操作。第一种类型是称为ACL(访问控制列表)的库的用途,第二种通常在每个项目中单独实现。

ACL 是一个函数(简单地说),它接收请求者用户和请求的操作(称为resource)并返回一个布尔值,指示是否允许用户执行该操作。准确地说,资源可以是复合的(如Files_DeleteFiles_Read)。ACL 功能需要说明谁可以做什么。大多数开发人员在用户通过身份验证时从永久存储(如数据库)加载这些数据,并将其存储在会话数据中,以防止从数据库重新加载。这是可能的,因为 ACL 数据不是那么大,并且可以将其存储在会话中。因此,通常使用 ACL 进行授权也不需要数据库访问(在创建之后)。

剩下的唯一讨论是当您想要检查请求者是否允许对请求的数据执行请求的操作时。通常这里的数据是指数据库的记录,通常有很多。因此,将如此大量的数据存储在数据库本身以外的任何地方都是不合逻辑的。而且由于它已经在数据库中,没有比 SQL 更适合查询谁可以在哪条记录上做什么的工具了。这是您需要访问数据库以验证用户请求的授权的地方。但在所有其他情况下,会话数据就足够了。

总之,在所有解释的场景中,只有一个需要数据库访问。其他的只能使用会话数据来完成。

于 2013-06-06T14:33:14.820 回答
1

检查用户是否通过会话登录是完全可以接受的,而且大多数人都是这样做的。在您的登录代码中,当用户成功通过数据库身份验证时,您可以在会话中设置一个标志来表示用户已登录。在随后的页面上,您可以在显示受保护的内容之前检查此标志。

// username and password match against the db
$_SESSION['user'] = array(
    'username' => 'xxx',
    'userId' => 'xxx',
    'loggedIn' => true;
);

检查他们是否已登录:

if(isset($_SESSION['user']) && $_SESSION['user']['loggedIn'])
{
    // good to show protected content
}

这样做并不一定意味着您必须将所有用户详细信息转储到会话中,您仍然可以为此进行实时查找。

于 2013-06-06T09:00:46.893 回答
0

虽然从本地文件中获取会话数据可能会快一点,但使用数据库更好地实现可伸缩性。

想象一个场景,您使用负载平衡器将流量分离到 Web 服务器的多个实例。如果数据存储在数据库(或集群)中,负载均衡器无需担心哪个 Web 服务器包含某个请求的会话数据。所有服务器都有它们。这使事情变得容易得多。

顺便说一句,您可以使用以下两种方法轻松切换session_set_save_handler()

于 2013-06-06T08:57:48.543 回答
0

如果您的目标只是检查用户是否已登录,并且 99% 的安全性(根据您的说法)是足够的,那么您当然可以这样做。但是,如果不从外部资源(或内部地图,但这不太可能)加载,您提到的姓氏检查是不可能的。

通常,您要做的不仅仅是检查用户是否登录。您加载用户的配置文件、角色、权限等...无论如何,您都必须访问数据库。在大多数情况下,最好在检查会话时加载用户并为页面请求的其余部分缓存信息,这样您就不会太频繁地访问数据库。

或者,在分布式环境中使用 APC 用户缓存存储或 Memcache 之类的东西。

于 2013-06-06T08:58:54.173 回答
0

您的会话数据存储在服务器session.save_path中 php.ini 文件中定义的位置,或者在运行时使用session_save_path存储在服务器中的会话数据存储在序列化文本中,如果人类可以访问服务器,则该文本可以读取。

为了让其他人劫持用户会话,他们需要知道session_id. 您可以查看PHP 会话 ID的唯一性以获取有关会话 ID 唯一性的更多信息。

要回答您的原始问题,是的,最好使用会话变量来测试用户是否已通过身份验证。这可以简单地通过检查会话变量的存在来完成,例如isset($_SESSION['auth_id']). 一定要session_destroy()在用户注销时和 session_regenerateid()` 销毁会话数据。

于 2013-06-06T08:59:26.887 回答
0

当您执行登录时,您可以为会话分配一些值。

<?php
// this should go right at the top of all pages using the session
session_start();

// other code

// your login procedure
if (login_condition == true)
{
    $_SESSION['logged_in'] = true;
    // other session values you want to store
}

然后,在您的仅登录页面中,您可以执行此操作

<?php
session_start();

if ( ! isset($_SESSION['logged_in']))
{
    header("Location: /path/to/login.php\r\n");
    exit;
}

如评论中所述,如果您需要在会话中删除用户怎么办?您仍然需要一些方法来对照数据库检查会话。

当然,如果这不是问题,请使用与上述类似的东西:)

于 2013-06-06T08:59:36.687 回答