PHP: 无法访问包含文件中的 $_SESSION

PHP: Can't access $_SESSION inside included file

我正在 PHP 中进行用户身份验证 class,在会话处理方面遇到了一些问题。

这是基础知识:

  1. global.php 我有一个名为 global.php 的文件,它包含在每个页面加载的开头。在这个文件中,我还包含了其他使用的 classes,例如我正在处理的 class.uservalidation.php。 我在 global.php 文件中启动会话。

  2. class.uservalidation.php 当这个 class 在 global.php 文件的开头实例化时,在构造函数中调用 checkLogin 方法检查会话变量 emailhash,如果它们匹配,它将 auth 属性 设置为所选用户的级别。

  3. login.php 是登录页面(显然...),提交后将调用用户验证 class 的 login 方法。此方法将在成功登录时设置两个会话变量,emailhash

  4. index.php为默认登录页面,会根据登录状态显示不同的内容

这是它如何工作的一个例子:

我去login.php。会话启动,classes 加载实例化。 checkLogin方法将首先报告auth=0。我提交表单并再次加载同一页面。 checkLogin 将在实例化 class 时再次报告 auth=0。然后在 login.php 脚本中调用 login 方法并设置会话变量。

但是...

当我只从 login.php 文件而不是 global.phpclass.uservalidation.php 执行 print_r($_SESSION); 时,我可以看到会话变量(即使这是我设置的地方会话变量)。

这是一个问题,因为我需要在后续页面加载时检查来自 loginCheck 方法的 emailhash 会话变量。

由于 $_SESSION 是一个超全局的,我认为它可以从任何地方访问,但我不知道哪里出了问题...

我觉得我在这里遗漏了一些非常基本的东西...我对 OOP 很陌生,所以可能是我遗漏了一些关于如何声明变量或其他东西的知识,但因为它是一个超全局的我以为没关系。

[编辑#1]

这里是一些代码(由于某些原因我不能粘贴到这个文本框中,所以我创建了指向 pastebin 的链接):

global.php:

<?php
// Load configuration
require 'config.php';

// Start secure session
session_start();

// Include libraries
require 'class.uservalidation.php';

//Connect to database

// Initialize user validation
$USER=new Uservalidation();

?>

class.uservalidation.php

<?php
/*
--------------------------------------------------------------------------------------
class.uservalidation.php
--------------------------------------------------------------------------------------
Based on example at http://www.wikihow.com/Create-a-Secure-Login-Script-in-PHP-and-MySQL

*/

class Uservalidation {
    public function __construct() {
        $this->data=FALSE;
        $this->auth=0;
        $this->loginCheck();
    }

    // login function is provided a hashed password directly from the browser (see uservalidation.js)
    public function login($email,$hash) {
        global $DB;
        if($email=filter_var($email,FILTER_VALIDATE_EMAIL)) {
            $email=$DB->real_escape_string($email);
            $hash=$DB->real_escape_string($hash);
            if($user=sql_fetch("SELECT * FROM users WHERE user_email='$email' AND user_hash='$hash' AND user_status>1 LIMIT 1")) {
                // Successful login
                $user_browser=$_SERVER['HTTP_USER_AGENT'];
                $_SESSION['session_email']=$user['user_email'];
                $_SESSION['session_hash']=hash('sha512',$user['hash'].$user_browser);
                $this->data=$user;
                $this->auth=$user['user_auth'];
                return TRUE;
            } else {
                // Email and hash does not match
                // Record attempt in login_attempts table
                return FALSE;
            }
        } else {
            // Not a valid email address
            return FALSE;
        }
    }

    public function logout() {
        $this->destroySession();
        $this->data=FALSE;
        $this->auth=0;
    }

    // Validate user session
    private function loginCheck() {
        $user_browser=$_SERVER['HTTP_USER_AGENT'];
        if($user=$this->getUser($_SESSION['session_email'])) {
            $user_hash=hash('sha512',$user['user_hash'].$user_browser);
            if(hash_equals($user_hash,$_SESSION['session_hash'])) {
                // Successful match
                $this->data=$user;
                $this->auth=$user['user_auth'];
                return TRUE;
            } else {
                // Hashes does not match
                return FALSE;
            }
        } else {
            // User doesn't exist
            return FALSE;
        }
    }

    // Get data for specific user (either by email, uid or hash)
    public function getUser($string) {
        global $DB;
        //echo "User: $string";
        if($email=filter_var($string,FILTER_VALIDATE_EMAIL)) {
            $checkuser=sql_fetch("SELECT * FROM users WHERE user_email='$email' LIMIT 1");
        } elseif($id=filter_var($string,FILTER_VALIDATE_INT)) {
            $checkuser=sql_fetch("SELECT * FROM users WHERE uid='$id' LIMIT 1");
        } else {
            $hash=$DB->real_escape_string($string);
            $checkuser=sql_fetch("SELECT * FROM users WHERE user_hash='$hash' LIMIT 1");
        }
        return $checkuser;
    }

    private function clearSession() {
        // Unset all of the session variables.
        $_SESSION=array();
    }

    private function destroySession() {
        $this->clearSession();

        // If it's desired to kill the session, also delete the session cookie.
        // Note: This will destroy the session, and not just the session data!
        if(ini_get("session.use_cookies")) {
            $params=session_get_cookie_params();
            setcookie(session_name(), '', time() - 42000,
                $params["path"], $params["domain"],
                $params["secure"], $params["httponly"]
            );
        }

        // Finally, destroy the session.
        session_destroy();
    }
}
?>

login.php

<?php
require_once 'global.php';

// Process login (field "p" is created in uservalidation.js and contain the SHA512 hash of the password so raw password will never be sent to server)
if(isset($_POST['user_email']) && isset($_POST['p'])) {
    if($USER->login($_POST['user_email'],$_POST['p'])) {
        //header('location:index.php');
        $html.="<pre>";
        $html.=print_r($_SESSION,TRUE);
        $html.=print_r($USER,TRUE);
        $html.="</pre>";
        $html.="<a href='index.php'>Go on!</a>";
    } else {
        $html="<p>Could not log in...</p>";
    }
} else {
    $theform=new htmlForm('login.php');
    $theform->addInput('Username',array('type' => 'email', 'name' => 'user_email', 'required' => '', 'autocomplete' => 'off'));
    $theform->addInput('Password',array('type' => 'password', 'name' => 'password', 'required' => ''));
    $theform->addInput(FALSE,array('type' => 'button', 'value' => 'Login', 'class' => 'button', 'onclick' => 'formhash(this.form);'));
    $html=$theform->render();
}

// Render Page
//=================================================================================================
?>

<!doctype html>
<html class="no-js" lang="en" dir="ltr">

<head>
    <meta charset="utf-8">
    <meta http-equiv="x-ua-compatible" content="ie=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>NGI Skunkworks</title>
    <link rel="stylesheet" href="css/foundation.css">
    <link rel="stylesheet" href="css/app.css">
    <link rel="stylesheet" href="css/icons/foundation-icons.css" />
</head>

<body>
<?php require '_menu.php'; ?>

<div class="row">
<br>
<div class="large-12 columns">
<?php echo $html; ?>
</div>
</div>

<script src="js/vendor/jquery.js"></script>
<script src="js/vendor/what-input.js"></script>
<script src="js/vendor/foundation.js"></script>
<script src="js/app.js"></script>
<script src="js/sha512.js"></script>
<script src="js/uservalidation.js"></script>
</body>

</html>

既然你不再回复评论中的问题,我将继续我的最新想法:

不要在 php-only 文件中使用结束 ?> php 标签。他们往往会引入被遗忘的空格,导致 HTML body 在您的代码中发送意想不到的点。

这些空格可能会导致 headers 在您开始 session 之前发送,这意味着 session-cookie 被 FUBARed。

另一个可能的原因是,由于 require_once,您的 global.php 文件没有包含在您认为包含的时候。我建议删除 _once 部分。


无关部分

There is much to unpack in this post, so what you get is a "stream of contentiousness" style of code-review.

你所拥有的是"include oriented programming"。我强烈建议您了解自动加载器。特别是 Composer.

附带的 PSR4 自动加载器

使用 sha512 哈希算法(尤其是未加盐的)是一个非常糟糕的主意。您应该学习使用(相对)新的 Password API.

用户电子邮件应该已经是唯一参数。在请求帐户详细信息时,附加 WHERELIMIT 条件毫无意义。

用户登录后,无需将登录凭据存储在session中。您应该只存储帐户 ID。

你的 classes 在构造函数中不应该有任何业务逻辑,因为这使得测试变得非常困难class。

而且你在整个代码库中都有 SQL 注入。您不应在查询中连接数据,这与您在 PHP 代码中不使用 eval() 的原因相同。