尝试理解使用PHP实现的Post/Redirect/Get设计模式

4

大家好,

提前道歉 - 我不是PHP专家,也不熟悉设计模式,所以这个问题可能有点基础...

无论如何,我正在开发一个需要登录的Web应用程序。

我的计划是这样的:

index.php:这个页面将包含一个简单的表格,允许用户输入用户名和密码。表格将把输入POST到...

login.php:这个页面将接收来自index.php的输入,并检查这些凭据是否与数据库匹配。如果任何输入缺失或凭证检查失败,php脚本将使用重定向将用户发送回index.php。

header('Location: http://www.mydomain.com/index.php');

如果凭据有效,则login.php会创建一个会话以建立用户的身份验证状态:
session_start();
$_SESSION['authenticated'] = true;

然后,它确定用户的访问类型。如果他具有“1级”访问权限,则脚本将使用以下方式将用户重定向到level1.php:
header('Location: http://www.mydomain.com/level1.php');

如果用户具有“2级”访问权限,脚本将使用以下方式将用户重定向到level2.php:
header('Location: http://www.mydomain.com/level2.php');

最后,当到达level1.php或level2.php时,它们首先要做的是检查会话。如果用户未经过身份验证,则将其重定向回index.php:

session_start();
if (!isset($_SESSION['authenticated']) {
    header('Location: http://www.mydomain.com/index.php');
} else {
    // proceed to display the page
}

在level1.php和level2.php中进行此检查将防止用户直接访问该页面,而不登录。我的第一个问题是:这个简单的逻辑第一次失败 - 当到达level1.php时,“isset($ _SESSION ['authenticated']”始终返回false,所以用户总是被重定向回index.php。如果他第二次输入完全相同的凭据,则该过程按预期工作。简言之,由于我不理解的原因,似乎login.php设置的会话在level1.php中找不到 - 我认为是因为重定向。换句话说,在未经往返到客户端浏览器的情况下,level1.php上的检查似乎会失败,直到/除非往返旅行被执行。由于每个需要登录的网站已经解决了这个问题,因此这不应该是一个新颖的挑战,并且应该有一个非常成熟的模式来解决它。我该如何处理它?相关问题...我以前看到过类似的问题,大多数答案通常涉及页面POST回自己的解决方案。这似乎有点奇怪 - 理想情况下,我希望每个PHP页面执行特定的工作:
  • index.php - 显示一个表单以捕获凭据,然后将它们发布到login.php
  • login.php - 评估用户的凭据,然后将他们引导到适当的页面
  • level1.php和level2.php - 显示适当的内容

这是一个有缺陷的设置吗?如果是,那么更好的设置是什么?

一般来说,如果一个页面建立了一个会话,然后将用户重定向到另一个页面,那么第二个页面是否有办法读取会话?

维基百科上有一个很棒的页面关于Post/Redirect/Get:

http://en.wikipedia.org/wiki/Post/Redirect/Get

但对我来说有点抽象,我想看到具体页面的参考说明:例如,“页面A”上的表单将数据POST到“页面B”,“页面B”将用户重定向到“页面C”等。
而且,如果在重定向时不识别会话,我不明白它是如何实现的。
非常感谢您提前的任何建议和见解。

[更新]

感谢Matt Ball的评论,我已经完善了这个问题:

login.php设置了会话并将用户重定向到下一个页面:

session_start();
$_SESSION['authenticated'] = true;
header('Location: http://www.mydomain.com/level1.php');

然而,当level1.php检查会话时,“authenticated”未被设置:
session_start();
echo (isset($_SESSION['authenticated']); // returns false

然而,如果我更改了login.php,使得header重定向到相对路径而不是绝对路径:

session_start();
$_SESSION['authenticated'] = true;
header('Location: level1.php');

然后,level1.php就像我期望的那样工作:
session_start();
echo (isset($_SESSION['authenticated']); // now returns true

我不明白为什么相对URL会有所不同,但它确实有所不同。所以,至少我的直接问题得到了解决。

非常感谢所有发表评论的人!


干杯, Matt Stuehler


5
关于会话问题的猜测:在 level1.php 中是否调用了 session_start();?另外,为了安全起见,不仅应该检查 level1...x.php 中的会话是否有效,还应该检查用户的级别是否足够。否则,Level1 用户可以登录,然后手动调用 level2.php。 - Select0r
1
你应该使用像Firebug+FireCookie这样的工具来检查当login.php发送其头文件时,PHPSESSID cookie是否实际设置,并且它们在之后也存在。 - SirDarius
2
我认为在用户登录后重定向回来之前,您需要开始会话。 - Phill Pafford
2
@matt:重定向时会“绝对”识别会话。HTTP重定向只是服务器发送给浏览器的一条消息,告诉它:“嘿,浏览器,请请求这个其他页面。” - Matt Ball
@SelectOr - 感谢您的建议。我在level1.php中调用了session_start()(我刚刚更新了原始帖子以反映此事),所以不幸的是,这不是它失败的原因。但是非常感谢您提出了关于同时检查级别访问权限的重要观点。 - mattstuehler
@Matt Ball - 感谢您的评论。这是我注意到的...当我使用绝对URL重定向时,会话无法识别。然而,当我使用相对URL重定向时,它可以被识别。您有任何想法为什么会出现这种情况吗? - mattstuehler
2个回答

1

Post Redirect Get(PRG)用于防止用户在提交表单后刷新被重定向到的页面时重新发送其POST数据。当您想要实现PRG时,应将HTTP头代码设置为303,如下所示:

header('Location: level1.php', 303);

应该先检查POST操作是否真的发生了。我更喜欢这段代码,因为它更完整:https://dev59.com/9W855IYBdhLWcg3wy3kb#4142969 - Buttle Butkus

0
如果凭证有效,则login.php创建一个会话以建立用户的身份验证状态:
我也在瞎猜,但我有印象你可能在login.php设置会话之前输出了一些内容。请在每个文件中将session_start作为第一条指令。

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