【问题标题】:PHP: Can't access $_SESSION inside included filePHP:无法访问包含文件中的 $_SESSION
【发布时间】:2017-05-14 14:16:28
【问题描述】:

我正在开发 PHP 中的用户身份验证类,但在会话处理方面遇到了一些问题。

这是基础:

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

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

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

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

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

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

但是……

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

这是一个问题,因为我需要在后续页面加载时通过 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();
    }
}
?>

登录.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>

【问题讨论】:

  • 那么您在包含其他文件之后开始会话。总是start_session() FIRST
  • 本题文字过多,代码不足。
  • 现在添加了代码示例的链接。无法粘贴到文本框中.....不,会话先于其他任何内容开始。
  • @MattiasOrmestad 您是否启用了完整的错误报告?
  • @tereško,我从 loginCheck 方法得到未定义的索引 (session_email)

标签: php oop session session-variables superglobals


【解决方案1】:

既然你不再回复 cmets 中的问题,那我就用我最新的想法来吧:

不要在 php-only 文件中使用关闭 ?&gt; php 标签。它们往往会引入被遗忘的空格,这是导致 HTML 正文在代码中意外发送的原因。

这些空格可能会导致在您开始会话之前发送标头,这意味着会话 cookie 得到 FUBARed。

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


无关部分

这篇文章有很多要解压的内容,所以你得到的是一种“争议流”风格的代码审查。

您所拥有的是“面向包括的编程”。我强烈建议您了解自动装载机。尤其是 Composer 附带的 PSR4 自动加载器。

使用 sha512 散列算法(尤其是无盐算法)是一个非常糟糕的主意。你应该学会使用(相对)新的Password API

用户电子邮件应该已经是一个唯一的参数。在请求帐户详细信息时,添加额外的 WHERELIMIT 条件是没有意义的。

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

您的类不应在构造函数中包含任何业务逻辑,因为这使得测试所述类变得非常困难。

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

【讨论】:

  • 感谢您花时间查看代码!我很抱歉我的回复延迟,我不得不清醒一点,所以我出去跑步......我真的很感谢你的代码审查,我只是一个自学成才的 n00b :D 我一直在想我应该看看作曲家,但还没有时间去做。
  • 您好!我删除了结束标签(对此一无所知......)并从require_once更改为require,我还清除了浏览器的缓存/cookies以确保。瞧,它奏效了!
  • 如果你来斯德哥尔摩,我会请你喝啤酒;)
  • 斯德哥尔摩的啤酒和里加的酒一样贵:P
猜你喜欢
  • 2013-02-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-04-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多