在页面之间传递数据的最佳实践
Posted
技术标签:
【中文标题】在页面之间传递数据的最佳实践【英文标题】:Best Practices for Passing Data Between Pages 【发布时间】:2011-03-13 13:33:44 【问题描述】:问题
在我们在项目之间重用的堆栈中,我们在会话中放置了过多的数据,以便在页面之间传递数据。这在理论上很好,因为它可以防止篡改、重放攻击等,但它会产生与解决的问题一样多的问题。
会话丢失本身就是一个问题,尽管它大部分是通过实现会话状态服务器(或使用 SQL Server)来处理的。更重要的是,要让后退按钮正常工作很棘手,而且要创建一个用户可以在三个选项卡中打开同一个屏幕以处理不同记录的情况也是额外的工作。
而这只是冰山一角。
这些问题中的大多数都有解决方法,但随着我的深入研究,所有这些摩擦让我觉得使用会话在页面之间传递数据是错误的方向。
我真正想做的是提出一个最佳实践,我的商店可以一直使用它在页面之间传递数据,然后,对于新应用,替换我们堆栈中当前依赖 Session 的关键部分。
如果最终解决方案没有导致大量样板管道代码,那也很好。
建议的解决方案
会话
如上所述,过度依赖 Session似乎 是个好主意,但它会破坏后退按钮并导致一些其他问题。
可能有办法解决所有问题,但似乎需要做很多额外的工作。
使用会话的好处之一是篡改不是问题。与通过未加密的 QueryString 传递所有内容相比,您最终编写的保护代码要少得多。
跨页发帖
事实上,我几乎没有考虑过这个选项。我对它使页面的耦合程度有疑问 - 如果我开始执行 PreviousPage.FindControl("SomeTextBox"),如果我想从另一个可能没有的页面访问此页面,这似乎是一个维护问题一个名为 SomeTextBox 的控件。
它似乎在其他方面也受到限制。例如,也许我想通过链接访问该页面。
查询字符串
我目前倾向于这种策略,就像过去一样。但我可能希望对我的 QueryString 进行加密以使其更难被篡改,并且我也想处理重放攻击的问题。
来自 Rolla 的 4 个家伙,there's an article about this。
但是,应该可以创建一个 HttpModule 来处理所有这些并从页面中删除所有加密香肠制作。果然,Mads Kristensen has an article where he released one. 但是,cmets 听起来像是在极其常见的场景中存在问题。
其他选项
当然,这不是对选项的详尽介绍,而是我正在考虑的主要选项。 This link 包含更完整的列表。我没有提到的那些,比如 Cookies 和 Cache 不适合在页面之间传递数据。
即将结束...
那么,您是如何处理页面之间传递数据的问题的呢?您必须解决哪些隐藏的问题,是否有任何预先存在的工具可以完美地解决这些问题? 你觉得你有一个完全满意的解决方案吗?
提前致谢!
更新: 以防我不够清楚,我所说的“在页面之间传递数据”,例如,将 CustomerID 键从 CustomerSearch.aspx 页面传递到Customers.aspx,将在其中打开客户并进行编辑。
【问题讨论】:
【参考方案1】:经过上述所有场景和答案以及此链接Data pasing methods 我的最终建议是:
COOKIES 用于:
ENCRYPT[userId's] 加密[productId] 加密[xyzIds..] 加密[等..]数据库用于:
按 COOKIE ID 的数据集 按 COOKIE ID 的数据表 所有其他大块按 COOKIE ID我的建议还取决于以下统计数据和此链接详细信息Data pasing methods:
【讨论】:
【参考方案2】:几个月后,我想我会用我最终采用的技术来更新这个问题,因为它效果很好。
在使用了更多涉及的会话状态处理(导致很多损坏的后退按钮等)之后,我最终滚动了自己的代码来处理加密的 QueryString。这是一个巨大的胜利——我的所有问题场景(后退按钮、同时打开多个选项卡、丢失会话状态等)都已解决,并且由于用法非常熟悉,因此复杂性极低。
这仍然不是万能的灵丹妙药,但我认为它对您遇到的大约 90% 的场景都有好处。
详情
我构建了一个名为 CorePage 的类,它继承自 Page。它有称为 SecureRequest 和 SecureRedirect 的方法。
所以你可以打电话:
SecureRedirect(String.Format("Orders.aspx?ClientID=0&OrderID=1, ClientID, OrderID)
CorePage 解析出 QueryString 并将其加密为一个名为 CoreSecure 的 QueryString 变量。所以实际的请求是这样的:
Orders.aspx?CoreSecure=1IHXaPzUCYrdmWPkkkuThEes%2fIs4l6grKaznFGAeDDI%3d
如果可用,当前登录的用户 ID 会添加到加密密钥中,因此重放攻击不是什么大问题。
从那里,您可以调用:
X = SecureRequest("ClientID")
结论
使用熟悉的语法,一切都可以无缝运行。
在过去几个月中,我还调整了此代码以处理边缘情况,例如触发下载的超链接 - 有时您需要在具有安全 QueryString 的客户端上生成超链接。这真的很管用。
如果您想查看此代码,请告诉我,我会将其放在某个地方。
最后一个想法:对于其他人在这里发布的一些非常周到的帖子,接受我自己的回答很奇怪,但这似乎确实是我问题的最终答案。感谢所有帮助我到达那里的人。
【讨论】:
正如评论加密本身并不能真正解决在 QueryString 中传递 ID 的安全性问题,它只是让它们更难调整。在一个安全的应用程序中,你倾向于在会话中拥有某种身份,比如用户名。然后,如果您有一个页面 Orders.aspx?OrderId=1,在您的较低层(服务/业务)中,您始终可以放入验证逻辑以确保通过此 id 检索到的订单实际上属于已登录的用户到应用程序。 @e36M3:好点。这个工具并没有说明你的 UserID 是如何存储的,它只是请求和重定向之上的一层。所以它当然可以这样使用,您只需将您的 UserID 存储在会话中并编写验证代码。我现在肯定在做验证部分,尽管我必须承认我将 UserID 存储在一个不完美的加密 cookie 中。但我想尝试完全不依赖会话。这些都不是高调的项目。总是有改进的余地.. @JorgeCode:好的,我会这样做的。 :)【参考方案3】:好的,所以我想以此作为我的回答开头;托马斯显然为新人提供了迄今为止最准确和最全面的答案。这个答案根本不一样。我的回答来自“业务开发人员”的立场。我们都非常清楚;有时花钱重写已经存在并且“有效”的东西是不可行的……至少不是一次性的。有时最好实施一种解决方案,让您随着时间的推移迁移到更好的替代方案。
我要说 Thomas 唯一缺少的是;客户端 javascript 状态。在我工作的地方,我们发现客户越来越期待“Web 2.0”类型的应用程序。我们还发现这类应用程序通常会带来更高的用户满意度。通过一些练习,以及一些非常棒的 javascript 库(例如 jQuery)的帮助(我们甚至开始使用 GWT,发现它非常棒)与 WCF 中实现的基于 JSON 的 REST 服务进行通信可能是微不足道的。这种方法还提供了一种非常好的方式来开始转向基于 SOA 的架构,以及 UI 和业务逻辑的清晰分离。
但我离题了。
在我看来,您好像已经有了一个应用程序,而且您已经超出了 ASP.NET 的内置会话状态管理的限制。所以...这是我的建议(假设您已经尝试过 ASP.NET 的进程外会话管理,它的扩展性比进程内/本地会话管理要好得多,听起来您有,因为您提到它); NCache。
NCache 为您提供了 ASP.NET 会话管理选项的“插入式”替代品。它非常容易实现,并且可以“创可贴”您的应用程序,足以让您通过 - 无需任何重大投资立即重构您现有的代码库。
您可以利用额外的时间和金钱来开始减少您的技术债务,方法是将新开发重点放在具有直接业务价值的事物上 - 使用新方法(例如其他答案中提供的任何替代方法,或我的方法)。
只是我的想法。
【讨论】:
sw12345:他 sw,感谢您的周到回答。我接受了它,因为它确实是我正在寻找的那种答案——托马斯对各种策略的概述是全面的,但我已经知道这些东西和事件在我的实际问题中提到了很多。我想要研究更高级的策略。谢谢!【参考方案4】:首先,您正在处理的问题与在无状态环境中处理状态有关。您遇到的困难并不新鲜,这可能是使 Web 开发比 Windows 开发或可执行文件开发更难的原因之一。
关于 Web 开发,据我所知,您有五种选择来处理用户特定的状态,它们都可以相互结合使用。您会发现没有一种解决方案适用于所有情况。相反,您需要确定何时使用每种解决方案:
查询字符串 - 查询字符串适用于传递指向数据(例如主键值)或状态值的指针。即使由于重放而加密,查询字符串本身也不应该被认为是安全的。此外,一些浏览器对 url 的长度有限制。但是,查询字符串有一些优点,例如可以将它们添加为书签并通过电子邮件发送给人们,并且如果不与其他任何内容一起使用,它们本质上是无状态的。
Cookie - Cookie 非常适合为特定用户存储非常少量的信息。问题是 cookie 也有大小限制,之后它只会截断数据,因此您必须小心将自定义数据放入 cookie。此外,用户可以终止 cookie 或停止使用(尽管这也会阻止使用标准会话)。与查询字符串类似,cookie 对于指向数据的指针比对于数据本身更好,除非数据很小。
表单数据 - 表单数据可能需要相当多的信息,但是会以发布时间和某些情况下的重新加载时间为代价。 ASP.NET 的 ViewState 使用隐藏的表单变量来维护信息。使用 ViewState 在页面之间传递数据的优点是可以更好地使用后退按钮,但可以轻松创建巨大的页面,从而减慢用户的体验。一般来说,ASP.NET 模型不适用于跨页发布(尽管有可能),而是适用于返回到同一页面并从那里导航到下一页的帖子。
会话 - 会话适用于与用户正在进行的进程相关的信息或常规设置。您可以以服务器内存或数据库加载时间为代价将大量信息存储到会话中。从概念上讲,Session 的工作原理是从内存或状态服务器一次为用户加载整个数据。这意味着,如果您有大量数据,您可能不想将其放入会话中。会话可能会产生一些返回按钮问题,这些问题必须与用户实际尝试完成的事情进行权衡。通常,您会发现后退按钮可能是 Web 开发人员的祸根。
数据库 - 最后一个解决方案(同样可以与其他解决方案结合使用)是您将信息存储在数据库中的相应架构中,并带有一个指示项目状态的列。例如,如果您正在处理订单的创建,您可以将订单存储在 Order 表中,并使用“状态”列确定它是否是真实订单。您可以将订单标识符存储在查询字符串或会话中。该网站将继续将数据写入表中以更新各个部分和子项目,直到最终用户能够声明它们已完成并且订单的状态被标记为真实订单。这会使报告和查询变得复杂,因为它们都需要区分“真实”项目和正在处理的项目。
您后面的链接中提到的一项是应用程序缓存。我不认为这是特定于用户的,因为它是应用程序范围的。 (它显然可以被硬塞到特定于用户的位置,但我也不建议这样做)。除了将数据传递给处理程序或模块之外,我从未在 HttpContext 中存储数据,但我怀疑它与上述解决方案有什么不同。
一般来说,没有一种解决方案可以统治它们。最好的方法是在每个页面上假设用户可以从任何地方导航到该页面(而不是假设他们通过使用另一个页面上的链接到达那里)。如果你这样做,后退按钮问题会变得更容易处理(尽管仍然很痛苦)。在我的开发中,我广泛使用前四个,有时在需要时使用最后一个解决方案。
【讨论】:
您好 Thomas,感谢您的回答。我接受了 sw12345 的回答,因为我并不是在寻找各种选项的优缺点,而是对有助于解决此问题的工具的一些高级见解,尤其是可以帮助我以透明方式自动执行查询字符串加密的工具。【参考方案5】:作为起点,我发现最好将关键数据元素(例如客户 ID)放入查询字符串中进行处理。您可以轻松跟踪/过滤来自这些元素的不良数据,它还允许与电子邮件或其他相关站点/应用程序进行一些集成。
在以前的应用程序中,查看员工或涉及他们的请求记录的唯一方法是登录应用程序,搜索员工或搜索最近的记录以找到有问题的记录。当相关部门的某个人需要对记录进行简单查看以进行审计时,这会产生问题并且会浪费大量时间。
在重写中,我通过“ViewEmployee.aspx?Id=XXX”和“ViewRequest.aspx?Id=XXX”的基本 URL 使员工 Id 和请求 Id 都可用。该应用程序设置为 A) 过滤掉错误的 Id 和 B) 在允许用户访问这些页面之前对用户进行身份验证和授权。这允许主要应用程序用户做的是向审计员发送简单的电子邮件,并在电子邮件中包含一个 URL。当他们非常着急时,他们处于批量处理时间,他们能够简单地点击一个 URL 列表并进行适当的处理。
其他与会话相关的数据,例如修改日期和维护用户与应用程序交互的“状态”会稍微复杂一些,但希望这可以为您提供一个起点。
【讨论】:
【参考方案6】:我永远不会这样做。在数据库中存储所有会话数据、根据用户 cookie 加载它时,我从来没有遇到过任何问题。就任何事情而言,这是一个会话,但我保持对它的控制。不要将会话数据的控制权交给 Web 服务器...
只需一点点工作,您就可以支持子会话,并允许在不同的选项卡/窗口中进行多任务处理。
【讨论】:
@Fosco - 你的班级在做什么,而正常的会话班级不是? ASP.NET 的会话允许您根据需要将其持久化到数据库中,但如果您不希望这样做,您可以在 Web 服务器的内存中持久化并获得更好的性能。 @Fosco- 顺便说一句,所有关于后退按钮和将大量数据填充到会话中的问题仍然存在,即使您滚动自己的会话类。 @Thomas - 这个问题中没有提到后退按钮,我不确定是否可以 100% 解决。在过去,我已经实现了一次性表单令牌来消除任何重复发布,如果这有帮助的话......就大量数据而言,我不得不不同意你的看法。 @Fosco - OP 的第二段中提到了后退按钮。如果您使用会话服务器将大量数据填充到会话中,这与将您自己的会话类存储到数据库中并在其中将大量数据填充到会话中有何不同?使用 ASP 和可能的 php(不确定)有自己的原因,但如果使用得当,ASP.NET 的 Session 与 ViewState 相结合实际上是一个很好的开箱即用组合。 @Thomas 我很好奇你如何处理页面之间的数据传递——也许你可以发布一个答案?我将这个问题重新命名为更多关于在页面之间传递数据的最佳实践,因为这正是我所追求的。以上是关于在页面之间传递数据的最佳实践的主要内容,如果未能解决你的问题,请参考以下文章