【问题标题】:PHP Session is not read properly on redirect重定向时未正确读取 PHP 会话
【发布时间】:2013-09-08 15:10:09
【问题描述】:

我在这里的第一篇文章。提前感谢您的任何答案:)

无论如何,我有一个登录脚本 (login.php),在提交凭据时它具有以下代码:

if(isset($_POST['submit'])) {

$password=$_POST['password'];
$email = (string)$_POST['email'];

try {
$db = new PDO('mysql:dbname=dbname;host=localhost', 'username', 'password'); // MY REAL DETAILS ARE USED HERE, JUST DON'T NEED THEM ON THE INTERNET
} catch (PDOException $e) {
    echo 'Connection failed: ' . $e->getMessage();
}
$sth = $db->prepare('SELECT * FROM members WHERE email = :email LIMIT 1');
$sth->bindParam(':email', $email);
$sth->execute();
$user = $sth->fetch(PDO::FETCH_OBJ);

// Hashing the password with its hash as the salt returns the same hash
if (crypt($password, $user->password) == $user->password) {
    if($user->id > 0) {
        if($user->status == 'Y' && date('Ymd', time($_SESSION['expiration'])) == date('Ymd')) {
            $data = array('status' => 'D');
            $update = mysql_update('members', $data, '`id`='.$_SESSION['memberID']);
        } 

        $status = mysql_select('SELECT status FROM members WHERE id='.$user->id);
        $_SESSION['memberID'] = $user->id;
        $_SESSION['expiration'] = $user->expiration_date;
        $_SESSION['timeout'] = time();

        if(isset($_REQUEST['url'])) header('Location: '.$_REQUEST['url']);
        else header('Location: account.php');
    } else {
        $error = "Whoops! That wasn't supposed to happen, please contact the webmaster.";
    }
} else {
    $error = "Sorry, something didn't quite match up. Please try again.";
    // print $user->password;
    // print "<br />";
    // print crypt($password, $user->password);
}
}

问题在于,当设置$_REQUEST['url'] 时,会发生重定向,但会立即发送回我的登录脚本。

示例:我的网址是mysite.com/login.php?url=account.php,我在登录时点击提交。它运行上面的代码,并直接将我发送回login.php,就好像没有设置会话一样。告诉account.php返回登录的代码是这样的:

if(!isset($_SESSION['memberID'])) header('Location: login.php?url=account.php');

我不明白为什么,但是当我硬刷新CTRL + F5 时,会话已设置,它会将我带回index.php,因为login.php

if(isset($_SESSION['memberID'])) header('Location: index.php');

我正在拔头发,因为这让我非常困扰。任何帮助表示赞赏。

如果你们好奇,我正在使用 PHP 版本 5.3.26。我知道我的会话何时设置,因为我有一个页面 (php.php) 可以打印出我与 var_dump($_SESSION); 的会话

谢谢!

编辑:我的问题不是重定向。从逻辑上讲,这是应该发生的事情,按顺序:

  1. 用户输入登录数据并点击提交
  2. 然后登录页面会检查他们的数据
  3. 设置会话变量
  4. 转到account.php

Account.php 应该查看我的会话是否设置if(!isset($_SESSION['memberID'])) header('Location: login.php?url=account.php'); 所以这一行是没有阅读我的会话的部分。

回到 login.php 设置会话:

$_SESSION['memberID'] = $user->id;
$_SESSION['expiration'] = $user->expiration_date;
$_SESSION['timeout'] = time();

if(isset($_REQUEST['url'])) header('Location: '.$_REQUEST['url']);
else header('Location: account.php');

这部分必须正常工作,因为正在设置会话并转到 account.php,但是 account.php 在设置时没有读取会话。为什么?

这也不是缓存问题,因为我已清除缓存并尝试限制对页面的缓存。

【问题讨论】:

  • 你会在脚本中的所有内容之前调用 session_start() 吗?
  • 我一定遗漏了一些东西,因为当您仅在 $_POST 发生的情况下检查它时,您如何获得 $_REQUEST['url'] ?您是否将其设置在您未提供的代码中?
  • @Roberto - 是 session_start() 在每一页上。
  • @Tim - mysite.com/login.php?url=account.php
  • 是的,但您正在 $_POST 内部寻找它。请参阅我的答案以获得澄清。

标签: php session


【解决方案1】:

你的事件顺序有点不对...

<?php
// you need session_start() at the top of each script in which you want to use sessions
session_start();

// check if the url parameter is passed *outside* of the isset($_POST['submit']) block
if(isset($_GET['url'])) {
    $url = strip_tags($_GET['url']);
    header('Location: '.$url);
} else {
    header('Location: account.php');
}

if(isset($_POST['submit'])) {
    // the rest of your stuff here...

这应该可以让您大致了解事件链...

login_form.php

if($_SERVER['REQUEST_TYPE'] == 'POST') {

    $password = strip_tags(trim($_POST['password']));
    $email = (string) strip_tags(trim($_POST['email']));

    // perform database validation here...

    // check if validation successful
    if($validated == true) {

        // set your sessions and other stuff here...

        // redirect to the account page
        header('location: account.php');
    } else {
        $error = 'Your email and password do not match';
        // page will display the form again and echo the error message
    }
}
?>

<?php
    if(isset($error)) {
        echo $error;
    }
?>
<form action="login_form.php" method="post">
    <input type="text" name="email">
    <input type="password" name="password">
    <input type="submit" name="submit">
</form>

account.php

<?php
session_start();

// put this at the top of EVERY page in which a user needs to be logged in

// see if user is logged in, if not, redirect to login form
if(!isset($_SESSION['memberID'])) {
    header('location: login_form.php');
}

编辑:在 hr 下添加了新的代码块

【讨论】:

  • 那我怎么不能把它拿到块里面呢?我不明白为什么。不过我会试试的……
  • 因为 $_GET 和 $_POST 的行为不同。如果您需要在 $_POST 块中包含它,则需要 $_GET url 参数并将其放入您的表单中(可能在隐藏字段中?),然后在 $_POST 期间阅读它。我建议你阅读使用 $_GET 和 $_POST:php.net/manual/en/reserved.variables.get.php
  • 好的,但是 $_REQUEST 是一个全局 $_GET、$_POST、$_COOKIE 变量,不是吗?此外,是否更改为 $_GET 并不能解决我的会话问题。 REDIRECT 不是问题,SESSION 没有被读取是我的问题。
  • 是的,但是在之前的评论中,您提到 login.php?url=account.php 是您传递 url 参数的方式 - 好吧,这不会在 $_POST 期间被读取(通常)。提交表单时,请查看地址栏中的 URL。它是否包含?url=account.php?可能不是。那么你将如何获得这个价值呢?您是否将 session_start() 放在每个脚本的最顶部?
  • 是的,这是因为我没有将表单操作设置到不同的页面。如果您创建一个没有操作的表单,它不会更改 url 格式。 session_start() 在每个页面上,因为我的会话正在工作,但 account.php 由于某种原因没有阅读它...
【解决方案2】:

首先确保您在应用的入口点上有这个:

if (!session_id()) session_start();

另外,在你做重定向的地方,做这样的事情(也总是根 URL 是好的):

header('Location: account.php');
die();

【讨论】:

  • 您能否详细说明为什么需要这样做? session_start() 需要被调用,而不考虑其他任何事情,不是吗?否则,您将无法访问任何会话明智的内容,因此我认为 if() 不会真正需要。此外,如果“逻辑上”标头已经将用户发送到重定向,为什么会在标头之后死掉。在我看来,代码在重定向时无论如何都会“死”......
  • 您总是必须调用会话开始或拥有一个包含该函数的配置文件,因为它是第一个函数调用。
  • @Jonast92 - 我就是这么想的。 Session_start() 是我每一页的第一件事,所以这不是问题。
  • @Minnow990 是的,我基本上是在支持这一点。没想到你是OP。重要的是我们意见一致。
猜你喜欢
  • 1970-01-01
  • 2011-07-26
  • 2023-03-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多