SQL Server、Internet 信息服务器和 ASP.NET 引擎都提供了坚实可靠的安全模型,它们可以很好地在一起协同工作。为了保证用户数据和应用程序的安全,Microsoft 还为每项服务的默认设置设置了相当低的值。大多数开发人员面临的挑战是如何使用 SQL Server、IIS 和 ASP.NET 在应用程序和数据之间设置适当的信任级别,而不会留下可被别人轻易攻入的安全漏洞。由于涉及三类服务(SQL Server、IIS 和 ASP.NET),所以需要采取三个关键的步骤来确保解决方案的安全。本部分讨论一种为 Web 应用程序设置足够权限和信任级别的更常用(且可靠)的方法。
注意:关于安全性和 Web 解决方案这个大主题,本系列文章难以展开较充分的讨论。要更好地理解此问题和可能的解决方案,请参阅安全 ASP.NET 应用程序的创建模式和实践系列文章:验证、授权和安全通信。 定义 DotNetKB 自定义 IIS 用户帐户。
保证 Web 应用程序安全性的最安全的方法是定义一个权限有限的自定义用户,然后对 IIS 进行配置,使之能够在执行您的 Web 应用程序时能作为自定义用户运行。这是相当容易实现的,可以确保访问您的 Web 应用程序的每个访问者都只具有您希望他们具有的权限。
第一步是生成一个新的 Windows 用户(本例中称为 DotNetKB),为其设置一个增强型密码,然后将其添加到 Windows 来宾组 (Guest Windows Group) 中。同时,确保选中 Password never expires(密码永不过期)和 User cannot change password(用户不能更改密码)复选框。这样将生成一个权限有限的用户,在 IIS 中运行您的 Web 应用程序时,您可以将其用作标识(参见图 7)。
图 7:生成的权限有限的用户
然后,调用 Internet 信息服务器管理员并选择承载这些网页的 Web 应用程序。在本例中,您可以选择承载前文所生成的测试页的 Web 应用程序 (DotNetKB_WebSite)。在树视图中的 Web 应用程序上单击鼠标右键,然后从上下文相关菜单中选择 Properties...(属性...)。然后选择 Directory Security(目录安全性)并单击该对话框 Anonymous access and authentication control(匿名访问和验证控制)部分中的 Edit(编辑)按钮。最后,输入自定义用户名 (DotNetKB),取消选择 Allow IIS to control password(允许 IIS 控制密码)复选框,并输入该自定义用户帐户的密码。完成所有这些工作之后,单击 OK(确定)按钮,将这些更改保存到 IIS 配置数据库中(参见图 8)。
然后,您需要为该自定义用户授予访问数据库 (DotNetKB) 的相应权限。为此,您可以使用 Microsoft SQL Server 企业管理器或编写一个自定义脚本,以创建一个这样的用户并授予其访问特定对象的权限。本文介绍如何使用 SQL Server 企业管理器完成此操作。您还可以从后文中看到一个脚本示例。
注意:尽管 Visual Studio .NET 2003 具有与 SQL Server 兼容的许多强大的集成功能,但也不允许从 Visual Studio .NET 2003 中轻松管理用户和用户权限。在大型的组织和团队中,这些高级任务通常由数据库管理员完成。
因此,启动 SQL Server 企业管理器之后,您可以按照以下步骤将自定义用户 (DotNetKB) 添加数据库中(参见图 9):
在左侧的树视图中,展开节点以显示 DotNetKB 数据库。在我的计算机上,树视图的结构如下:Console Root | SQL Server Group | (LOCAL) (Windows NT) | Databases | DotNetKB。 然后,在数据库下的 Users(用户)节点上单击鼠标右键,并选择 New Database User...(新建数据库用户...)。显示 Database User Properties - New User(数据库用户属性 - 新建用户)对话框时,从 Login name(登录名)下拉框中选择 (<新建>)。
显示 SQL Server Login Properties - New Login(SQL Server 登录属性 - 新建登录)对话框时,选择 General(常规)选项卡,并在 Name(名称)输入框中输入 DotNetKB。确保选中 Windows Authentication(Windows 验证)单选按钮,并从 Domain(域)下拉框中选择自定义用户帐户所在的计算机的名称。然后从 Database(数据库)下拉框中选择 DotNetKB。 现在,选择 Databases(数据库)选项卡,在对话框顶部的列表中找到 DotNetKB 数据库并选中它。然后,确保选中对话框底部列表中的 public(公共)角色。最后,单击对话框底部的 OK(确定)按钮,保存您的更改。
请注意,您只需添加 元素并将模拟特性设置为 true(真)。不必输入用户帐户或密码,因为这些信息将由 IIS 提供。也就是说,即使其他人能够读取您的配置文件,他们也无法确定使用哪些标识凭据来执行您的 Web 应用程序。
至此,您已生成了自定义用户,并为其设置了访问 SQL Server 和 IIS 的相应权限。现在,我们来创建一些测试页,确保它能够正常工作。从这里您可以看出一切正常。 创建 ASP.NET 测试页
创建测试页始终是访问 SQL Server 数据层并验证输入和输出参数是否得到正确处理的好办法。实际上,这是确保以后的生产解决方案中的 ASP.NET 页和组件能够按照预期方式工作的唯一办法。这对于从解决方案中的某个层调用其他层时的验证信任边界和安全性问题尤其正确。
另外,在进行测试时,请勿拘泥于创建生产类接口。您只需测试目标方法。实际上,故意创建一些您不愿以之为最终生产解决方案的“丑陋”测试页是一个好的策略!本文中,我创建了一些非常简单的 ASP.NET 页,其中包含一个测试记录列表和一个用于添加、编辑和删除测试记录的输入表单。
例如,以下是用于测试主题记录的 WebForm 布局。您会发现,它包含错误消息或其他消息的状态标签、记录计数标签、显示记录列表的数据网格、用于输入检索时使用的记录 ID 的输入框以及支持添加、编辑和删除记录的小表格(参见图 10)。
图 10:用于测试主题记录的 WebForm 布局
在创建测试页时,最好使代码简洁明了。我通常会为每个按钮添加一小段代码,以调用本地方法来处理数据库操作。以下是 TopicTest.aspx 页上 Get Record(获取记录)按钮的代码。
Private Sub btnGetTopic_Click(ByVal sender As System.Object,ByVal e As System.EventArgs) Handles btnGetTopic.Click Try Dim ID As Integer = Int32.Parse(txQueryID.Text) GetItem(ID) ' 进行数据库调用 txID.Text = txQueryID.Text txTitle.Text = mTitle txDescription.Text = mDescription lbStatus.Text = "success!" Catch ex As Exception lbStatus.Text = ex.Message End Try End Sub
Private Sub GetItem(ByVal ID As Integer) Try pr = New SqlParameter("RETURN_VALUE", SqlDbType.Int) pr.Direction = ParameterDirection.ReturnValue Dim pTitle As SqlParameter = New SqlParameter With pTitle .Direction = ParameterDirection.Output .DbType = DbType.String .ParameterName = "@Title" .Size = 30 End With Dim pDescription As SqlParameter = New SqlParameter With pDescription .Direction = ParameterDirection.Output .DbType = DbType.String .ParameterName = "@Description" .Size = 500 End With
cd = New SqlCommand
With cd .CommandText = "TopicsGetItem" .CommandType = CommandType.StoredProcedure .Parameters.Add(New SqlParameter("@AdminCode", "adm")) .Parameters.Add(New SqlParameter("@ID", ID)) .Parameters.Add(pTitle) .Parameters.Add(pDescription) .Parameters.Add(pr) .Connection = cn .Connection.Open() .ExecuteNonQuery() .Connection.Close() End With
' 检查返回代码 If Not pr.Value Is Nothing Then Select Case Int32.Parse(pr.Value) Case 100 : Throw New ApplicationException("Access violation") Case 101 : Throw New ApplicationException("Invalid ID") End Select End If ' 设置返回值 mTitle = pTitle.Value.ToString() mDescription = pDescription.Value.ToString() Catch ex As Exception Throw New Exception(ex.Message, ex) End Try End Sub
对于本解决方案示例,我最终生成了六个 Web 表单,并用它们测试了将近 30 个存储过程和自定义函数。您可在本文开始部分列出的下载软件包中找到所有这些完成的表单。
现在我们已经定义了表、创建了存储过程和函数并生成了 ASP.NET Web 表单,因此可以使用 Visual Studio .NET 2003 生成数据库层的安装脚本了。数据库管理员(有时是您自己)可以将此脚本应用到生产服务器上。 生成源代码和安装脚本
Visual Studio .NET 的另一个重要功能是它能够为现有数据库生成一个完整的生成脚本。实际上,您可以使用 Visual Studio .NET 为整个数据库层生成源代码(包括生成表和索引、授权、存储过程等),还可以生成一个可用于在现有 SQL Server 上安装这些数据库对象的命令行脚本。
生成安装脚本非常容易,它包括两个步骤:首先,需要生成 T-SQL 脚本来创建数据库对象(表、索引、过程等)。然后,生成一个针对目标 SQL Server 执行 T-SQL 脚本的命令行脚本。
使用此脚本和 T-SQL 文件集,现在您可以将新数据库层安装到任何您具有相应权限的目标 SQL Server 2000 上了。
小结
本部分讨论了很多内容。包括如何使用 Visual Studio .NET 2003 创建数据库项目,如何创建新数据库以及定义表、索引、约束条件和关系的数据库图,还介绍了使用 Visual Studio .NET 2003 添加可以存取表中存储的数据的存储过程和自定义函数。通过本文的学习,您还学会了如何使用自定义的 Windows 帐户以及 IIS 和 Web 应用程序中的 web.config 文件设置,在 SQL Server 和您的 ASP.NET 解决方案之间建立一种可靠的信任关系。本文最后还介绍了用于验证数据层程序设计的测试 Web 表单示例,并说明了如何生成可用于在任何目标 SQL Server 上安装完成的这个数据层的 T-SQL 脚本和命令行脚本。
也许您已经注意到,数据库层的相关讨论中未涉及到专家记录的存储和再调用过程。这是因为我决定使用 XML 文件代替它。这样,我们可以借此机会学习如何将 XML 作为数据源,以及如何将这种数据格式与 SQL Server 数据结合起来以创建一个完整的解决方案。在下一部分中,我们将定义 XML 存储格式和读写这种数据的组件层,还将学习有关 XML 序列化以及 ASP.NET 中的内置数据高速缓存服务的相关知识。