如何在 PHP 中检查用户是否登录?

60

我对PHP还比较陌生,正在尝试使用会话(sessions)来检查用户是否已登录网站,以便他们可以获得访问特定页面的权限。

这是一件复杂的事情吗?或者只是因为我是新手所以无法弄明白?


7
是的,但我正在寻找有人能以我能理解的方式来解释它,所以谢谢! - Andrew
8个回答

118

登录并不太复杂,但是几乎所有登录过程都需要一些特定的部分。

首先,请确保在所有需要知道已登录状态的页面上启用会话变量,将以下内容放置在这些页面的开头:

session_start();

接下来,当用户通过登录表单提交他们的用户名和密码时,您通常会通过查询包含用户名和密码信息(如MySQL)的数据库来检查它们。如果数据库返回匹配项,则可以设置一个会话变量来包含该事实。您可能还想包括其他信息:

if (match_found_in_database()) {
    $_SESSION['loggedin'] = true;
    $_SESSION['username'] = $username; // $username coming from the form, such as $_POST['username']
                                       // something like this is optional, of course
}

接下来,在依赖于登录状态的页面上加入以下内容(不要忘记session_start()):

if (isset($_SESSION['loggedin']) && $_SESSION['loggedin'] == true) {
    echo "Welcome to the member's area, " . htmlspecialchars($_SESSION['username']) . "!";
} else {
    echo "Please log in first to see this page.";
}

这些是基本组件。如果您需要有关SQL方面的帮助,互联网上有很多教程可供参考。


2
谢谢您的解释。我想知道是否可以将文件夹限制而不是页面?这样,除非您已登录,否则我在该文件夹中拥有的任何页面都会受到限制。 - CodeMonkey

30
几乎所有这个页面上的答案都依赖于检查会话变量的存在来验证用户登录。这完全没问题,但需要考虑到如果在同一台服务器上有多个虚拟主机/站点,则PHP会话状态对于您的应用程序并不是唯一的。
如果您在Web服务器上有两个PHP应用程序,都使用名为“isLoggedIn”的会话变量中的布尔标志检查用户的登录状态,则用户可以登录其中一个应用程序,然后自动获得访问第二个应用程序而无需提供凭据。
我怀疑即使是最老式的商业共享托管也不会允许虚拟主机以这种方式共享相同的PHP环境,从而跨多个客户网站发生此类情况(不再是)。但这是要考虑到的东西。
最简单的解决方案是使用标识应用程序的会话变量,而不是布尔标志。例如 $SESSION["isLoggedInToExample.com"]。
来源:我是渗透测试员,有很多关于如何不做事情的经验。

这是一个有效的观点。在大多数情况下,您会使用额外的Session变量(例如用户令牌)存储会话登录布尔标志,以便在提供访问权限之前完全验证用户。但正如之前所述,$SESSION ["isLoggedInToExample.com"]比$SESSION ["loggedIn"]更好,但我希望在授予访问权限之前还验证了唯一字段。 - Jeacovy Gayle
我认为以上内容已足以验证对网站的访问权限,因为它来自于一个cookie... 暗示着某种基于表单的身份验证已经发生。除此之外,您仍然可以执行其他检查,以实现更细粒度的用户访问控制;这取决于您运行的网站类型。 - hiburn8
2
会话信息有多安全?我知道它存储在服务器上的 tmp 目录中。攻击者能否修改会话数据以登录自己?这是我应该关心的问题吗?此外,我应该避免将加密密钥存储在会话中吗? - Indiana Kernick
我认为你在这里提出了一个新问题,@IndianaKernick。会话数据是安全的,因为它不在Web根目录中。但它可能永远不是加密密钥的合适位置。对于任何敏感信息,您需要具有适当访问控制的数据库。即使使用会话存储缓存用户数据也存在风险,如果它是PII。通常最好返回到数据库或寻找替代解决方案。 - hiburn8

12

您希望对任何需要执行会话检查的页面进行操作,都需要以以下方式开始:

session_start();

从那儿开始,你需要检查你的会话数组中是否有一个变量指示他们已经登录:

if (!$_SESSION["loggedIn"]) {
    redirect_to_login();
    die;
}

将用户登录,实际上就是设置该值:

$_SESSION["loggedIn"] = true;

1
在检查当前会话之前,您需要在所有页面上添加此内容:
session_start();

检查$_SESSION["loggedIn"]是否不是true - 如果不是,则将其重定向到登录页面。

if($_SESSION["loggedIn"] != true){
    echo 'not logged in';
    header("Location: login.php");
    exit;
}

0

您可以创建一个会话并将其放置:

// Start session
session_start();

// Check do the person logged in
if($_SESSION['username']==NULL){
    // Haven't log in
    echo "You haven't log in";
}else{
    // Logged in
    echo "Successfully logged in!";
}

注意:您必须创建一个包含$_SESSION['username'] = $login_input_username;的表单。


0
除了@hiburn8的精彩答案之外,缺少的是一个安全的注销方式。我意识到这不是被问到的问题,但也应该考虑。
注销应包含两个元素:
1. 从会话变量中删除所有数据 2. 重置ID以防止会话重用尝试。
也许如果所有会话数据都被销毁,则不需要2),但可能会有(未来的)情况需要这样做。幸运的是,PHP为这两个元素提供了一个函数,完整的代码如下:
session_start();             // get a session to manipulate
session_unset();             // remove all server data on the session
session_regenerate_id(true); // create a new session id to prevent hijacking

这通常应该放在注销脚本中。


顺便说一句,这个答案与 @hiburn8 的精彩答案直接相悖,后者明确指出会话可能包含登录特定站点的信息以外的其他数据。因此,必须具体处理,而不是销毁整个会话。 - Your Common Sense

-3
请查看此脚本以进行注册。它非常简单易懂。
<?php
    define('DB_HOST', 'Your Host[Could be localhost or also a website]');
    define('DB_NAME', 'database name');
    define('DB_USERNAME', 'Username[In many cases root, but some sites offer a MySQL page where the username might be different]');
    define('DB_PASSWORD', 'whatever you keep[if username is root then 99% of the password is blank]');

    $link = mysql_connect(DB_HOST, DB_USERNAME, DB_PASSWORD);

    if (!$link) {
        die('Could not connect line 9');
    }

    $DB_SELECT = mysql_select_db(DB_NAME, $link);

    if (!$DB_SELECT) {
        die('Could not connect line 15');
    }

    $valueone = $_POST['name'];
    $valuetwo = $_POST['last_name'];
    $valuethree = $_POST['email'];
    $valuefour = $_POST['password'];
    $valuefive = $_POST['age'];

    $sqlone = "INSERT INTO user (name, last_name, email, password, age) VALUES ('$valueone','$valuetwo','$valuethree','$valuefour','$valuefive')";

    if (!mysql_query($sqlone)) {
        die('Could not connect name line 33');
    }

    mysql_close();
?>

请确保使用phpMyAdmin来处理所有数据库相关的事务。这是一个非常易于使用的工具。您可以在此处找到它:phpMyAdmin


2
这不仅没有回答原问题,而且还是一个糟糕的例子。该脚本容易受到SQL注入攻击,并且从个人角度来看,我不会向任何人推荐phpmyadmin。 - Chris J
除了@ChrisJ的意见外,我只能表示同意。除此之外,没有对设置的$POST条目进行任何检查。即使将mysql替换为mysqli,它在开发/测试环境中也是一个不错的示例,但在生产中,它是糟糕的代码。__不要使用!_ - theking2

-5
else if (isset($_GET['actie']) && $_GET['actie']== "aanmelden"){

    $username= $_POST['username'];
    $password= md5($_POST['password']);
    $query = "SELECT password FROM tbl WHERE username = '$username'";
    $result= mysql_query($query);
    $row= mysql_fetch_array($result);

    if($password == $row['password']){
            session_start();
            $_SESSION['logged in'] = true;
            echo "Logged in";

    }
}

1
这段代码存在SQL注入漏洞,2019年不应该将密码存储为MD5哈希值,它使用了已弃用的PHP函数,并且没有任何解释。这是一个糟糕的答案,应该被删除。 - hiburn8
1
另外,由于没有错误检查...可能会强制将$row和$password值设置为FALSE,从而绕过登录。我刚刚检查了一下,md5()在失败时返回FALSE...因此,将密码值作为数组而不是字符串发送将绕过它。 - hiburn8
1
即使作为蓝图,这也让我感到非常愤怒。只有在密码正确的情况下才会调用session_start()。那么对于已登录但以某种方式试图使用错误凭据再次登录的人怎么办?他们会在下一个响应中被踢出,但是如果他们导航到另一页,他们会神奇地再次登录吗?session_start() 应该存在其他地方。 - hiburn8
1
我可以写一本关于为什么我讨厌这个答案的书。 - hiburn8

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接