检查用户是不是使用会话而不是数据库查询登录

Posted

技术标签:

【中文标题】检查用户是不是使用会话而不是数据库查询登录【英文标题】:Check if user is logged in with session instead of database query检查用户是否使用会话而不是数据库查询登录 【发布时间】:2013-06-02 04:47:39 【问题描述】:

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

如果他的ID存在于数据库中,为每个用户(想想成千上万的用户)在每个子页面上查询数据库是不是有点过分?

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

【问题讨论】:

您的数据库是不是太慢了以至于实际上要花很多钱来查找它?通常不应该这样。 99% 的应用程序无论如何都需要从数据库中获取一些用户特定的东西。所以这不是一个额外的查找,而是一个你无论如何都无法避免的查询。 数据库是用来访问的,对吧? @Pekka:不,不是,但我认为最好建立一个稳定且面向未来的基础,这样我以后就不用担心了。因为随着时间的推移会有越来越多的数据库查询... 这取决于你的用例,真的。如果您采取预防措施并正确保护会话,我认为依赖会话并不是一件坏事。检查这个问题:***.com/questions/328/php-session-security 【参考方案1】:

虽然从本地文件中获取会话数据可能会稍微快一些,但使用数据库的可扩展性更好。

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

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

【讨论】:

【参考方案2】:

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

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

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

【讨论】:

【参考方案3】:

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

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

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

【讨论】:

【参考方案4】:

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

<?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;

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

当然,如果这不是问题,请使用类似于上面的内容:)

【讨论】:

【参考方案5】:

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

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

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

【讨论】:

【参考方案6】:

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

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

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

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

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

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

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

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

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

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

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

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

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

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

【讨论】:

感谢您的深入了解。你的回答对我来说似乎合乎逻辑。

以上是关于检查用户是不是使用会话而不是数据库查询登录的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 JSTL 检查会话中是不是存在已登录用户?

如何在中间件中使用会话来始终检查用户是不是登录

检查会话是不是存在 JSF

如何检查用户是不是已经使用 Apache Shiro 登录?

使用带有令牌而不是 Cookie 的 Django 会话框架?

检查用户是不是在模板中使用 Flask-Login 登录