【问题标题】:What is the best way to persist an object using forms in PHP?在 PHP 中使用表单持久化对象的最佳方法是什么?
【发布时间】:2010-10-11 16:39:12
【问题描述】:

我有一个 PHP 应用程序,我希望某些对象以下列方式持久化:

  1. 该对象不得存在于 $_SESSION 中。单独的 Web 浏览器窗口必须控制对象的单独实例。
  2. 最终用户不得通过手动更改 $_REQUEST 变量的内容来修改对象(如果发生这种情况,则应将请求视为已损坏)。

是否有最佳实践/正确的方法来做到这一点?随着 PHP 越来越面向对象,我担心我正在重新发明轮子。

这段代码的主要目的是允许在不使用数据库的情况下创建和操作复杂对象,直到它们被提交,然后我将使用适当的事务将它们完全提交到数据库。我想让我的数据库只包含完整的发票,或者根本不包含发票。

我目前的方法如下:

<?php

include('encrypt.php');
include('invoice.class.php');

if(isset($_REQUEST['invoice']))
{
    $invoice = unserialize(decrypt(base64_decode($_REQUEST['invoice'])));
    if(!($invoice instanceOf invoice)) throw new exception('Something bad happened');
}
else
{
    // Some pages throw an exception if the $_REQUEST doesn't exist.
    $invoice = new invoice();
}

if(isset($_REQUEST['action']) && $_REQUEST['action'] == 'addLine')
{
    $invoice->addLine(new invoiceLine($_REQUEST['description'], $_REQUEST['qty'], $_REQUEST['unitprice']);
}

?>
<form action="index.php" method="post">
<input type="text" name="qty" />
...
<input type="hidden" name="invoice" value="<?php echo(base64_encode(encrypt(serialize($invoice)))); ?>" />
</form>

【问题讨论】:

  • 问题在标题中,但我已经把它放到了q的正文中。对此感到抱歉。

标签: php oop object persistence


【解决方案1】:

这里有个窍门:把它放在饼干里!

简洁的算法:

$data = 序列化($object); $时间=时间(); $signature = sha1($serverSideSecret . $time . $data); $cookie = base64("$signature-$time-$data");

好处是你

a) 可以在需要时使 cookie 过期,因为您将时间戳用作签名哈希的一部分。

b) 可以验证数据没有在客户端被修改,因为您可以从 cookie 中的数据段重新创建哈希。

此外,如果 cookie 太大,您不必将整个对象存储在 cookie 中。只需将您需要的数据存储在服务器上,并使用 cookie 中的数据作为密钥即可。

我不能把算法归功于这个算法,我是从 Flickr 成名的 Cal Henderson 那里学到的。

编辑:如果您发现使用 cookie 过于复杂,请忘记它们,并将本应进入 cookie 的数据存储在隐藏的表单字段中。

【讨论】:

  • 我喜欢这种方法,但它遇到的问题与会话中多个窗口控制同一个 cookie 的问题相同。
  • Cookie 是特定于文档的,您可以使用 javascript 管理它们。只要在服务端计算 SHA1 签名(对哈希盐保密),剩下的就可以在客户端用 JS 做,并确保每个 cookie 中都有一个 documentId。
  • 为了清楚起见,您可以使用单个域路径存储多个 cookie。只需确保每个窗口都有自己的 cookie。
  • 这对我来说听起来很合理,如果不是比我目前的方法更复杂的话。 +1
  • 我想这有点复杂,但是签署序列化对象并让用户存储它有一些简单的优雅。
【解决方案2】:

您还可以使用简单的隐藏表单输入在客户端保存状态,无需 cookie。只要数据(可能是序列化的 blob)经过加密和签名,用户就无法在不中断会话的情况下对其进行修改。

Steve Gibson 将这种方法用于他的定制电子商务系统。虽然他的代码不是开源的,但他在Security Now Episode #109,“GRC 的电子商务系统”中彻底解释了保存状态的方法,无需在服务器上存储敏感数据或需要 cookie 支持。

【讨论】:

  • 很好的答案。 Security Now 第 109 集的第 14 页记录包含我想要的信息。他和我做的一模一样。
  • 在客户端存储加密数据之前,还请阅读:stackoverflow.com/questions/606179/…
  • 除非我弄错了,否则该帖子表明您可以不费吹灰之力地暴力破解 AES 256,这是一个可笑的假设。地球上没有任何力量可以用精心挑选的密钥和良好的盐来暴力破解 AES 256。即使加密被破解,签名也能防止篡改。
  • “破坏 AES 256”部分被高估了,但答案的主题是:除非没有其他方法,否则不要向客户端发送数据。
  • 我仍然不一定同意这一点。如果它被正确加密和签名,它就像在您的服务器上一样安全。作为奖励,对于电子商务应用程序,它使您不必在服务器上存储敏感数据(如 CC 号码),因此 CYA 更容易。
【解决方案3】:

我要做的是将加密 key(不是整个结构,就像您的示例那样)存储在隐藏的表单变量中。此键是您存储不完整发票的uncreated_invoices 表的索引。

在页面之间,您更新 uncreated_invoices 表中的数据,完成后,将其拉出并提交。如果您将用户名放入 uncreate_invoices 表中,这也会让他们从上次中断的地方继续(不确定这是否是一个有效的用例)。无论如何,将用户名放在其中可能是个好主意,这样人们就不会试图劫持其他人的发票。

如果您愿意,您可能可以在 uncreated_invoices 表中存储序列化数据。就个人而言,我会对其进行一些规范化(多少取决于您的架构),以便您可以轻松添加/删除各个部分。

编辑: 我忘了提到你应该定期清理uncreated_invoices 表,这样它就不会被过期的发票填满。

【讨论】:

  • 这是一个很好的答案。然后我将不得不定期清理未创建的发票表(人们可能会放弃会话),并在每次页面加载时从表中构建我的对象。 +1
  • 哦,我完全忘记了!谢谢!
【解决方案4】:

您不能将数据存储在 $_SESSION 中的数组中,然后为每个窗口设置一个唯一的 ID。如果每个窗口都有唯一的 id,您可以将 id 作为表单的一部分传递。根据窗口 ID 存储/检索会话或数据库中的数据。

所以是这样的吗? $_SESSION['data'][$windowid]->$objectname

【讨论】:

  • 是的,我能做到。但是我目前的方法是幂等的。 +1
【解决方案5】:

如果您不能使用 SESSION,那么您必须自己持久化数据。您必须将数据放置在可以持久保存的位置,例如数据库或其他文件。它是除了 SESSION 之外的唯一方法来持久化数据。

我收回这一点,您可以在每个 HTML 页面中发送数据并在本地使用它。每个接受数据的页面都必须创建延续数据序列的 HTML/Javascript。

【讨论】:

    【解决方案6】:

    如果您将内容存储在客户端,则可以对其进行修改。是否有您不想将对象存储在会话中的特定原因?如果存储对象实际上不可行,则需要将其持久保存在服务器上的其他位置。

    【讨论】:

    • 如果您打开了多个窗口,将对象保留在会话中将导致两个窗口修改同一个对象。这是我的应用程序无法接受的。
    • 让每个窗口在会话中创建一个新索引。或者为服务器上的每个对象创建一个文件
    【解决方案7】:

    没关系,他们将能够添加他们想要的所有项目。您的代码中的问题是您从请求中获取项目描述符、数量和价格,价格确实应该在后台查找,否则,用户可以手工制作他们的价格。哎呀,负值物品,您可以免费获得东西。

    【讨论】:

    • 示例应用程序不是为了向用户销售东西,而是让用户在线创建自己的发票并保留记录。
    【解决方案8】:

    你想存储一些东西。这通常在数据库中完成。如果不在数据库中查找,您如何知道某物的价格?所以使用相同的数据库来存储有关当前用户/会话的信息。

    【讨论】:

      【解决方案9】:

      您可以使用 magic methods __sleep()__wakeup()

      【讨论】:

        猜你喜欢
        • 2010-12-29
        • 2016-04-01
        • 2010-09-28
        • 1970-01-01
        • 1970-01-01
        • 2010-10-06
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多