PP团队圣经巨著《Application Architecture Guide2.0》24章->Web程式开发向导
第二十四章 Web程式原型
目标
l 学习Web程式的通常设计考虑点。
l 学习Web程式的主要原则。
l 学习Web程式的层指导原则。
l 学习性能,安全以及部署指导原则。
概览
Web程式的核心是服务器端的逻辑。它可能由很多不同的层组成。典型的例子就是三层架构,表现层,业务层以及数据层。下面的图表列举了通用的Web应用架构,由一些不同领域的通用组件组成。
设计考虑
设计安全和高性能的Web应用的主要目标是将任务分解成不同的关注点以减少复杂性。设计时请考虑以下原则:
l 把应用逻辑分区。将应用逻辑分为表现层,业务层和数据层。这将提高代码可维护性以及允许监控和优化每一层的性能。一个清晰的分层可以提供程式更好的扩展性。
l 在层间使用抽象来实现松耦合。如同输入和输出Facade可以将请求转换成层组件理解的格式。而且,你可以使用接口类型或抽象基类来定义共享的抽象,以被接口组件实现。
l 明确组件之间如何通信。这要求你首先要了解程式所支持的部署环境。你必须确定通信是要跨越物理边界,进程边界还是所有组件都在同一个进程中。
l 减少数据包往返。当设计Web程式,考虑使用缓存,输出缓冲来减少浏览器和服务器,Web服务器和下载服务器之间的数据包往返。
l 考虑使用缓存。一个好的缓存策略也许是与性能关系最大的设计考虑点。Asp.net的缓存包括输出缓存,局部页缓存以及缓存API,设计时请好好利用这些优势。
l 考虑使用日志和监控组件。你必须审核和记录穿越程式层之间的活动。这些日志可以用来检测可疑的活动,通常可以在早期发现对系统的攻击行为。
l 避免长时间运行的任务锁。如果你有长时间运行或加锁的操作,考虑使用异步的方法以允许Web服务器处理其它输入请求。
l 鉴别所有穿越信任边界的用户。比如,当从表现层访问远程的业务层时。
l 不要在网络间传输没加密的敏感数据。如果你需要通过网络传递密码或授权cookie等敏感信息,考虑加密以及签名数据或者使用SSL。
l 使用最小权限账户来运行Web程式。如果攻击者利用一个进程,而进程被限制访问文件系统和其它系统资源,这样会减少可能的损害。
Web程式结构
Category |
Key Issues |
Authentication |
在信任边界之间缺少验证。 |
|
在数据库中使用普通文本格式来存储密码。 |
|
设计自定义的验证机制来代替内置的机制。 |
Authorization |
在信任边界间缺少授权。 |
|
不正确的角色粒度。 |
|
在不需要的时候使用代理。 |
Caching |
缓存了易变的数据。 |
|
没有考虑缓存页面输出。 |
|
缓存敏感数据。 |
|
没有缓存数据为ready-to-use格式。 |
Exception Management |
对终端用户暴露了敏感信息。 |
|
没有记录异常的完整信息。 |
|
用异常来处理应用程式逻辑。 |
Logging and Instrumentation |
没有在所有层实现适当的监控。 |
|
没有记录严重系统事件和关键业务事件。 |
|
不支持运行时的日志和监控配置。 |
|
记录了敏感信息。 |
Navigation |
在用户接口混合了导航逻辑。 |
|
硬编码视图联系。 |
|
没有核实用户是否被授权访问视图。 |
Page Layout(UI) |
对于复杂的布局使用基于表格的布局。 |
|
设计了复杂的重载页面。 |
Page Rendering |
使用过多的回传影响用户体验。 |
|
使用过大的页面降低了性能。 |
Presentaion Entity |
在不需要的时候创建了自定义的实体对象。 |
|
将业务逻辑添加到了表现层实体。 |
Request Processing |
混合了处理逻辑和呈现逻辑。 |
|
选择了不适当的模式。 |
Service Interface Layer |
破坏了服务接口。 |
|
在服务接口里实现了业务规则。 |
|
没有考虑互操作性需求。 |
Session Management |
使用了不正确的状态存储。 |
|
没有考虑串行化需求。 |
|
在需要的时候没有维护状态。 |
|
对大量数据如Dataset使用ViewState。 |
Validation |
依赖客户端验证。 |
|
在信任边界间缺少验证。 |
|
没有重用验证逻辑。 |
验证
不适当的或较弱的验证可能会导致你的程式易受欺骗攻击,字典攻击,会话劫持以及其它类型的攻击。
当设计验证策略时,考虑以下原则:
l 识别Web程式层之间的信任边界。这将帮助你决定哪里需要验证。
l 使用平台支持的验证机制,如Windows验证。
l 如果你使用Forms验证,尽可能利用平台特性。
l 如果你使用Forms验证,保护你的授权cookie。
l 如果你没有使用SSL,考虑缩短session过期时间以降低授权cookie的暴露。
l 在Web程式中将公共区和限制区分开。
l 加强账户管理,如使用账户锁定和过期。
l 使用强密码策略。包括特定的密码程度和复杂度,以及密码过期策略。
l 不要在数据库或数据源中存储密码,应该存储hash加密过的密码。
l 保护信任的存储区。
授权
不适当的或较弱的授权会导致信息暴露,数据被篡改以及权限被提升。深度防御是程式授权策略的关键安全原则。 设计时考虑以下原则:
l 识别Web层的信任边界。
l 对页面和目录访问控件使用URL授权。
l 使用Asp.net角色管理来进行角色授权。
l 如果查找角色进程比较消耗资源,考虑缓存角色信息。
l 考虑授权设置粒度。
l 使用加密来保护授权过的cookie,或者设置HttpOnly属性来防止客户端脚本访问。
l 访问下载资源时,用信任的子系统模型来实现身份识别。
l 如果使用代理来指定特定的授权和访问粒度,要考虑对性能和扩展性的影响。
缓存
缓存可以提高应用程式的性能和响应。但是,错误的缓存选择和低劣的缓存设计可以降低性能和响应。你应该使用缓存来优化数据查找,避免网络数据包往返,避免不必要和重复的处理。为实现缓存,你必须决定何时加载缓存数据。使用异步加载缓存或使用批处理可以避免客户端延迟。
当设计缓存时,可以考虑以下原则:
l 避免为每一个用户缓存数据。
l 缓存全局数据或者多用户使用的数据。
l 避免缓存易变数据。
l 使用output缓存来缓存相对静态的页面。
l 对页面中的静态用户控件数据使用局部页面缓存。
l 选择何时的缓存位置,如客户端,代理服务器或Web服务器。
l 贫困的共享资源是昂贵的,如网络连接,可以使用缓存。
l 缓存数据用ready-to-use格式。
l 对于大量缓存,考虑用异步单线程加载或者使用批处理进程。
l 考虑在表现层缓存要展示给用户的数据。
l 当数据不能有效的从数据库检索的时候,考虑在业务层缓存它。
l 如果需要缓存大量的数据较长时间,那么将它缓存在数据库里。
l 避免使用分布式的聚合的缓存。
异常管理
设计有效的异常管理对于系统的安全性和可靠性非常重要。在Web页面上正确的异常处理可以阻止将敏感的异常详细信息展现给用户,可以提高系统的稳固性以及避免系统不一致状态的发生。
当设计异常管理策略时,考虑以下原则:
l 不要用异常来控制逻辑流程,并且避免你的代码发生异常。
l 除非你处理它,否则不要捕获异常。
l 集中异常处理方案。
l 对于不可料的错误使用全局的错误处理。
l 对终端用户使用友好的异常信息。
l 不要在异常明细里显示敏感信息。
l 设计合适的异常传播策略。
l 设计合适的异常记录策略。
l 要记录异常的详细信息。
l 设置异常管理系统以在错误发生的时候也要保证安全。
记录和监控
l 考虑使用平台特性,如异常检测来记录和审核事件。
l 集中设置日志和监控机制。
l 考虑审核用户管理的事件。
l 审核不正常的活动。
l 审核关键业务操作。
l 创建安全日志文件管理策略。
l 不要在日志或审核文件里存储敏感信息。
导航
设计导航策略时,要把它与处理逻辑分开。它帮助用户快速浏览网站,设计一致的导航结构可以降低系统的使用复杂性。
当设计导航策略时,考虑以下原则:
l 使用众所周知的模式,如MVC,可以将UI和复杂的导航逻辑解藕。
l 考虑在MasterPage中封装导航,这样可以跨页面保持一致性。
l 在小的Web应用程式中,可以考虑将导航封装于控件中。
l 如果使用Menu,考虑使用.net的SiteMap Provider。
l 设计SiteMap可以帮助用户查找页面,同时也允许搜索引擎抓取站点。
l 如果在窗体间使用向导来实现导航。
l 对于信息丰富且类似于树状管理的站点可以使用有层次的导航,如图书馆。
l 使用可视的元素,如链接,导航菜单等以帮助用户迅速的找到站点可用的东西。
l 设计保存导航状态策略。
页面布局(UI)
设计程式时,要将页面布局和特定的UI组件及UI处理过程分开。当选择了一种布局策略,考虑是设计者还是开发者创建此布局。如果设计者创建此布局,那么选择不需要编码的布局方法或使用专门的开发工具。
当设计布局策略时,考虑以下原则:
l 确定是否要支持跨浏览器。
l 在任何可能的布局使用CSS。
l 当你需要支持网格布局,那使用基于表格的布局,但是要牢记表格布局可能呈现较慢,而且不能完全的跨浏览器,并且在布局复杂的时候可能会有问题。
l 如果你要在网格或以表格样式来展现数据,那么使用HTML 表格来帮助弱视人群或其它特别的用户。
l 使用统一的布局可以尽可能的提供易用性。
l 在ASP.NET里使用MasterPage为所有的页面提供一个统一的视觉效果和感受。
l 避免设计和开发大型页面来完成多个功能,特别是通常一个请求只会调用很少的任务。
l 分割页面内容可以提高缓存效率和减少呈现。
页面呈现
当设计页面呈现时,要保证页面呈现的效率和接口的最大利用。请遵循以下原则:
l 尽量减少传递的页面大小,例如,在不需要的地方Disable掉ViewState。
l 考虑使用数据绑定选项。例如,可以为控件绑定自定义对象或DataSet。但是,数据绑定只能在ASP.NET里使用。
l 使用AJAX来提高用户体验和获取较好的响应。
l 对大数据量使用分页技术。
l 设计全球化策略。如为字符串数据使用资源文件。
l 考虑在用户接口组件里设计本地化。
l 将用户处理组件从数据呈现和获取程式里抽象出来。
表现实体(Presentation Entity)
在表现层里使用表现实体存储数据以管理视图表现实体不一定是必须的。只有当DataSet非常大或者复杂时,才使用表现实体将数据单独从UI控件中分离出来存储。设计或选择合适的表现实体可以很容易的与UI控件进行绑定。
设计表现层实体时,考虑以下原则:
l 决定你是否需要表现层实体。
l 考虑使用自定义的类来将控件直接映射到业务实体。
l 考虑表现层实体的串行化需求。
l 避免将业务逻辑添加到表现实体中。
l 考虑在表现实体中实现输入数据验证。
l 考虑使用表现实体来存储用户接口状态。
请求处理
当设计请求处理策略时,你要将请求处理逻辑从用户接口分离出来,以分离每一个关注点。
当设计请求处理策略时,考虑以下原则:
l 统一实现页面请求处理前后逻辑以提高重用。例如,创建一个基类派生于Page类,并且包含了请求处理前后逻辑。
l 考虑将UI处理分成三类,Model,View以及Controller/Presenter,通过使用MVC或MVP模式。
l 为提高可测试性,避免在View和Model之间使用Passive View模式(MVP模式的一种)。
l 如果你在设计需要处理大量数据的视图,考虑View访问Model时使用Supervising Contorller模式(MVP模式的一种)。
l 如果程式不依赖于viewstate,而且也没有很多控件事件,考虑使用MVC模式。
l 如果包含复杂的导航和命令处理需求,考虑使用Front Controller模式(MVC的一种)。
l 不要在视图中实现请求处理。
l 在合适的地方使用过滤器模式来实现可拆卸的过滤器。
l 考虑使用事件来通知调用者更改状态数据。例如,使用观察者模式。
l 确保在请求处理逻辑中没有混合业务规则。
会话管理
当设计Web程式时,一个有效的和安全的Session管理策略对于性能和可靠性非常重要。你要考虑Session管理的一些因素,如存放什么,存放位置以及信息保存时间。
当设计Session管理策略时,考虑以下原则:
l 不要依赖客户端状态管理。
l 选择合适的会话状态存储,如进程,Asp.net StateServer,或者SQLServer。
l 如果你有一个Web服务器,需要最适宜的会话性能以及并发会话相对较少,可以使用在进程内存储会话状态。
l 如果你的Session重建代价非常昂贵,并且在Asp.net重启的时候还会保持,那么在本地的WebServer上使用会话状态服务。
l 如果你首先考虑的是可靠性,那么将会话存储在SQLServer中。
l 对于Web farm可以使用远程的会话状态服务或Sqlserver状态存储。
l 保护你的会话状态通信渠道。
l 限制访问会话状态数据。
l 对于会话数据使用基本类型以减少串行化花费。
l 使用平台特性来加密Cookie中的Session ID。
l 如果客户端浏览器禁用cookies,使用无cookie的Session。
验证
设计一个有效的验证方案对于程式的安全性和可靠性非常重要,请遵循以下原则:
l 识别Web程式层之间的信任边界,并且验证所有穿越边界的数据。
l 假定所有客户端的数据都是恶意的。
l 设计验证策略以限制,拒绝和清除所有恶意的数据。
l 验证输入长度,范围,格式和类型。
l 验证所有输入,如查询字符串,Cookie和Html控件。
l 不要只依赖Asp.net 请求验证。
l 考虑使用Asp.net 验证控件。
l 在Asp.net中使用正则表达式来限制输入。
l 不要显示不信任的输入。
l 如果需要输出不信任的数据,用HTML编码输出。
l 为提高用户体验使用客户端验证,以及在服务器端再次验证以提高安全。
l 避免用户输入文件路径和名称。
表示层考量
Web程式的表现层提供了用户接口和用户交互。设计必须集中于分离关注点,将用户交互逻辑和用户接口组件解藕。
当设计表现层时,考虑以下原则:
l 考虑将用户接口组件和用户接口处理组件分离。
l 评估表现层和业务及数据层的交互方式。
l 对于客户端和服务器端都使用输入验证策略。
l 设计数据格式和显示策略,包括对用户的视觉样式。
l 使用页面输出缓存换局部缓存来缓存静态页或页面的一部分。
l 使用观察者模式来处理用户事件。
l 使用Page Controller模式将业务层和表现层分离。
l 当你有复杂的页面导航逻辑并且需要动态配置时,使用Front Controller模式。
l 如果需要将控件编译进程序集以重用,或你需要对现存的服务器控件添加额外特征,使用Web服务器控件。
l 如果需要重用一些页面的局部UI或需要缓存页面的特定部分,使用Web用户自定义控件。
业务层考量
当设计Web程式的业务层时,考虑怎样实现业务逻辑和长时间运行的工作流。设计业务实体代表真实世界数据,以及使用它来在组件间传递数据。
当设计业务层时,考虑以下原则:
l 设计一个单独的业务层来实现业务逻辑和工作流。这将提高程式的可维护性和可测试性。
l 考虑集中和重用通用业务逻辑功能。
l 设计无状态的业务层。这将帮助减少资源争夺及提高性能。
l 对于业务实体使用粗粒度的包,如DTO。
l 业务组件要高内聚,低耦合。
l 对重要的业务操作使用事务。
l 决定同步或异步执行业务流中的处理步骤。
数据层考量
为Web程式设计一个抽现访问数据库的数据层。使用单独的数据层将使程式容易配置和维护。可以使用服务代理来使数据层访问外部服务。
当设计数据层时,考虑以下原则:
l 设计一个单独的数据层以隐藏数据库的详细信息。
l 使用实体对象在层间传输数据和交互。
l 对不同类型的数据存储选择合适的数据访问技术。
l 利用连接池来减少打开的连接数量。
l 设计异常处理策略来处理数据访问错误,并且将异常抛转到业务层。
l 考虑使用批处理操作来减少数据库的数据包往返。
服务层考量
如果你计划远程部署业务层或者使用Web Service来暴露业务逻辑,考虑使用单独的服务层。
当设计服务层时,考虑以下原则:
l 设计粗粒度的服务方法以减少客户端和服务器的交互,以及提供松耦合。
l 不要认为只有一种客户端来访问服务。
性能考量
在Web程式的早期设计阶段,通过获取非功能的需求来识别性能对象。响应时间,吞吐量,CPU,内存以及磁盘IO是必须考虑的关键因素。
注意以下原则:
l 保证性能需求是特定的,现实的和灵活的。
l 使用非分布式部署来提高性能。
l 实现缓存技术以提高程式性能和扩展。
l 执行批处理操作来减少边界的数据包往返。
l 避免在表现层进行原子事务操作,这会降低可扩展性。
l 在进程里存储会话信息。
l 减少在服务器和客户端之间传输的HTML数据。
l 避免不必要的网络数据包往返。
安全考量
安全对于保证数据的一致性和机密性非常重要。你需要为Web程式设计一个安全策略,使用已测试和证明的安全解决方案,以及实现审核,授权和数据验证,以保护系统免受威胁。
考虑以下原则:
l 在任何的信任边界使用审核。
l 考虑使用强大的授权机制以限制资源访问和保护业务逻辑。
l 考虑使用输入和数据验证以防止跨站脚本攻击和代码注入等安全威胁。
l 不要依赖客户端验证。
l 考虑加密和签名任何通过网络的敏感数据。
部署考量
当部署Web程式时,你需要考虑层和组件的位置对性能,扩展和安全的影响,另外也要设计取舍。使用分布式或非分布式的部署取决于业务需求和Infrastructure限制。
部署时考虑以下原则:
l 考虑使用非分布式部署来提高性能。
l 考虑使用分布部署来实现可扩展性和提高每层的安全性。
非分布式部署
在非分布式部署的环境里,Web程式的各个逻辑层都放置在相同的Web服务器上,除了数据库。你必须考虑程式怎样处理多个并发用户,以及怎样保证在同个Server上其它层的安全。
考虑以下原则:
l 如果你不需要和其它程式共享业务逻辑考虑使用非分布式的部署。
l 如果Web程式对性能要求较高,可以使用非分布式的部署,因为从本地调用其它层会提供额外的负担。
l 为你的业务层使用基于组件的接口。
l 如果你的业务逻辑运行在同一个进程里,避免在业务层审核。
l 考虑使用可信任的身份来访问数据库(通过信任的子系统Model)。这可以提高性能和系统可扩展性。
l 考虑加密以及数字签名Web服务器和数据库间传递的敏感数据。
分布式部署
在分布式部署环境里,Web程式的表示层和业务层逻辑在单独的物理层,并且需要远程通信。通常需要把业务和数据访问层放置于同一服务器。
当选择分布式部署时,考虑以下原则:
l 除非需要否则不要将业务逻辑组件单独放置。
l 如果你的业务逻辑被其它程式共享,考虑使用分布式部署。
l 如果为了安全考量,禁止在前端Web服务器上部署业务逻辑的话,考虑使用分布式部署。
l 在表现层和业务层之间使用防火墙以增加安全性。
l 表现层不需要初始化,参与原子事务。
l 为业务层使用基于消息的接口。
l 使用TCP协议和业务层通信可以获取更好的性能。
l 在不同的物理层间保护敏感信息传输。
l 如果业务层也被其它程式访问,考虑在业务层使用审核。
负载平衡
当在多台服务器上部署你的Web程式,你可以使用负载平衡来分派请求以保证它们可以被多个Web服务器处理。这将帮助提高响应,资源利用以及吞吐量。
如果设计Web程式使用负载平衡,考虑以下原则:
l 设计可扩展的Web程式时避免太倾向于Server。请求从服务器发出,也被服务器处理。这种现象经常发生于你使用本地可更新的缓存,或者进程中或本地存储会话状态。
l 考虑为Web程式设计无状态的组件;例如,一个Web前段没有进程状态和有状态的业务组件。
l 考虑使用Windows网络负载平衡(NLB)作为一个软件方案来实现请求的重定向。
Web Farm考量
Web farm允许扩展程式,并且尽量减少硬件故障的冲击。当你增加更多的服务器,你可以使用负载平衡或者集群方法。
考虑以下原则:
l 考虑使用集群来减少硬件错误的影响。
l 如果程式要求高输入和输出需求,考虑通过多数据库服务器来分区数据库。
l 考虑配置Web farm来将同一个用户的所有请求路由到同一个Server以提供亲和性。
l 当同一个用户的请求不能保证被路由到同一个服务器时,在web farm不要使用进程内的会话管理。使用进程外的状态服务器服务或数据库服务器。
模式映射
Category |
Scenarios |
Authentication and Authorization |
Brokered Authentication |
|
Direct Authentication |
|
Federated Authentication (Single Sign On or SSO) |
|
Trusted Sub-System |
Caching |
Cache Dependency |
|
Page Cache |
Exception Management |
Exception Shielding |
Logging and Instrumentation |
Provider |
Navigation |
MVP |
|
MVC |
Page Layout |
Template View |
|
Composite View |
|
Transform View |
|
Two Step View |
Request Processing |
Page Controller |
|
Front Controller |
|
Passive View |
|
Supervising Controller |
关键模式
l Composite View-将单个视图合并为一个组合的表现层。
l Exception Shielding –对外部系统或用户过滤异常数据。
l Front Controller – 将所有请求装入管道以通过一个处理对象,它可以在运行时被装饰者修改。
l Modal View Controller – 将用户接口和数据存放以及两者间的代码逻辑分离开来。
l Modal View Presenter – 将用户接口,数据存放以及代码逻辑分离。
l Page Cache – 使用页面缓存来提高频繁访问的动态Web页面(页面不经常变化以及构建时消耗了系统的大量资源)的响应时间。
l Page Controller – 将页面的输入请求,交给一个特定的页面或Action来处理。
l Template View – 实现一个通用的模板视图,然后从它派生或创建视图。
技术考量
基于微软平台,从Asp.net出发,你可以在Asp.net Web窗体中融合大量的技术,如Asp.net Ajax,Asp.net MVC,SilverLight,以及Asp.net Dynamic Data:
l 如果通过Web浏览器来访问Web程式,考虑使用asp.net web窗体。
l 如果Web程式需要加强交互性或后台处理,考虑使用Asp.net Ajax。
l 如果Web程式包含富多媒体的内容和交互性,考虑使用SilverLight。
l 如果Web程式需要实现以控制为中心的模型,将控制器分离以及提高可测试性,考虑使用Asp.net MVC。
l 如果设计Web程式需要基于数据开发,考虑使用Asp.net Dynamic Data。
patterns & practices Solution Assets
• Web Client Software Factory at http://msdn.microsoft.com/en-us/library/bb264518.aspx.
• Building Secure ASP.NET Applications at http://msdn.microsoft.com/enus/
library/aa302415.aspx.
• Improving Web Application Security at http://msdn.microsoft.com/enus/
library/ms994921.aspx.
Additional Resources
• For more information on design patterns for Web applications, see Enterprise Solution
Patterns Using Microsoft .NET at http://msdn.microsoft.com/en-us/library/ms998469.aspx.
• For more information on designing and implementing Web client applications, see Design
and Implementation Guidelines for Web Clients at http://msdn.microsoft.com/enus/
library/ms978605.aspx.
• For more information on designing distributed Web applications, see Designing Distributed
Applications at http://msdn.microsoft.com/en-us/library/aa292470(VS.71).aspx.
• For more information on Web application performance issues, see Improving .NET
Application Performance and Scalability at http://msdn.microsoft.com/enus/
library/ms998530.aspx.
• For more information on Web application security, see Improving Web Application Security:
Threats and Countermeasures at http://msdn.microsoft.com/en-us/library/ms994921.aspx.