带有 service pack 6.0a 或更高版本的 microsoft® windows nt® 4.0 workstation
带有 service pack 6.0a 或更高版本的 microsoft® windows nt® 4.0 server
microsoft® windows® 2000 professional
microsoft® windows® 2000 server
microsoft® windows® 2000 advanced server
microsoft® windows® 2000 datacenter server
microsoft® windows® xp home edition
microsoft® windows® xp professional
microsoft® windows® server 2003 系列
注意 在所有这些系统上,还要求有 microsoft® internet explorer 5.01 或更高版本以及 microsoft® windows® installer 2.0 或更高版本。
服务器 带有 service pack 2.0 的 microsoft® windows® 2000 professional 带有 service pack 2.0 的 microsoft® windows® 2000 server
带有 service pack 2.0 的 microsoft® windows® 2000 advanced server
带有 service pack 2.0 的 microsoft® windows® 2000 datacenter server
microsoft® windows® xp professional
microsoft® windows® server 2003 系列
附加软件要求 若要使用附加功能,如 asp.net、com+ 服务和 sql server .net 数据提供程序,需要下列附加软件。
方案 功能 所需软件 客户端 sql server .net 数据提供程序 microsoft 数据访问组件 (mdac) 2.6 或更高版本 对系统管理信息的访问 windows management instrumentation (wmi)(在 windows 2000、windows millennium edition 和 windows xp 上随操作系统一起安装) com+ 服务 windows 2000 service pack 2.0 服务器 sql server .net 数据提供程序 microsoft 数据访问组件 (mdac) 2.7 asp.net microsoft internet 信息服务 (iis) 5.0
默认情况下,在已经安装了 .net framework 的计算机上安装新的 .net framework 时,所有的 asp.net 应用程序都会自动更新以使用新安装的 .net framework 版本。唯一的例外是那些绑定到不兼容的运行库版本或更新的运行库版本的应用程序。尽管更高版本的 .net framework 具有向后兼容性,您仍可能需要相应地配置 asp.net 应用程序以使用某种旧版本。以下各节描述了针对 .net framework 的特定版本配置 asp.net 应用程序的过程。
查看 asp.net 应用程序的脚本映射
在管理安装了多个版本的 .net framework 的计算机时,查看 asp.net 应用程序的脚本映射,以确定应用程序所使用的版本,这样做通常很有用。可以使用 internet 信息服务管理控制台查看 asp.net 应用程序的脚本映射。
查看 asp.net 应用程序的脚本映射
打开 iis 管理控制台,单击加号展开本地计算机,然后定位到包含 asp.net 应用程序的文件夹。 右击文件夹,再单击“属性”。出现应用程序的“属性”对话框。 单击“目录”选项卡中的“配置”按钮。出现“应用程序配置”对话框。 在“映射”选项卡中,选择 asp.net 应用程序扩展名,如 .asmx 或 .aspx。 对话框的“可执行文件路径”列列出了到应用程序所使用的 asp.net isapi 版本的路径。默认情况下,asp.net isapi 被安装在以下位置:
系统根/microsoft.net/framework/版本号
路径中显示的版本号说明了应用程序所使用的 asp.net isapi 的版本号。asp.net isapi 版本确定了应用程序所使用的运行库版本。
使用 aspnet_regiis.exe 更新脚本映射
为使重新配置 asp.net 应用程序的脚本映射更容易,每个 .net framework 安装都提供了一个关联的 asp.net iis 注册工具 (aspnet_regiis.exe) 版本。管理员可以使用此工具将 asp.net 应用程序重新映射到与此工具关联的 asp.net isapi 版本。
注意 因为 aspnet_regiis.exe 链接到特定版本的 .net framework,管理员必须使用适当版本的 aspnet_regiis.exe,以针对 asp.net 应用程序重新配置脚本映射。aspnet_regiis.exe 仅将 asp.net 应用程序的脚本映射重新配置为与此工具关联的 asp.net isapi 版本。 此工具也可用来显示所有已安装的 asp.net 版本的状态、注册关联的 asp.net 版本、创建客户端脚本目录以及执行其他配置操作。
当前映射到要卸载的 asp.net 版本的所有 asp.net 应用程序将被重新映射到计算机上的次最新兼容版本。如果不存在相兼容版本,该应用程序的脚本映射将被完全删除。 警告 删除脚本映射后,iis 将以文本形式提交 asp.net 页。这有可能会将源代码公开给客户端。 asp.net 状态服务为 asp.net 的所有兼容版本所共享,并且总是映射到安装的最新版本的 asp.net。如果您要卸载的 asp.net 版本正是当前的最新版本,计算机上的下一个兼容的最高版本就会被注册,以取代这一版本。如果不存在兼容版本,asp.net 状态服务将被卸载。 特定于要卸载的版本的性能对象及关联的性能计数器将被删除。适用于所安装的所有 asp.net、asp.net 和 asp.net 应用程序版本的一般性性能对象和计数器将定向到计算机上剩下的最新安装版本。 aspnet 用户帐户为 asp.net 的所有兼容版本所共享。如果计算机上已不存在其他的 asp.net 安装,aspnet 用户帐户以及关联的访问控制列表将被删除。
asp.net 应用程序的脚本映射
如果在同一计算机上安装了多个版本的 .net framework,则这些安装各包含一个关联的 asp.net isapi 版本。asp.net 应用程序使用 asp.net isapi 确定该应用程序使用哪一版本的 .net framework。asp.net 应用程序可以配置为使用所安装的 asp.net isapi 的任一版本。若要指定 asp.net 应用程序所使用的 asp.net isapi 版本,应在 internet 信息服务 (iis) 中为该应用程序注册脚本映射。
脚本映射将文件扩展名和 http 谓词与适当的 isapi 相关联,以便实现脚本处理。例如,如果 iis 接收到对某一 .aspx 文件的请求,该应用程序的脚本映射就会指示 iis 将所请求的文件发送给适当版本的 asp.net isapi 来处理。每一 asp.net 应用程序的脚本映射通常是在 internet 信息服务管理控制台中设置的,可以直接应用到某一应用程序,也可以从父应用程序继承。默认情况下,安装了 .net framework 之后,会自动更新计算机上所有现有 asp.net 应用程序的脚本映射以使用与这一安装相关联的 asp.net isapi 版本,除非应用程序使用更高的版本或者不兼容的版本。
为使重新配置 asp.net 应用程序的脚本映射更容易,每个 .net framework 安装都提供了一个关联的 asp.net iis 注册工具 (aspnet_regiis.exe) 版本。默认情况下,该工具安装在以下目录:
<系统根>/microsoft.net/framework/〈版本号〉
管理员可以使用此工具将 asp.net 应用程序重新映射到与此工具关联的 asp.net isapi 版本。
注意 因为 aspnet_regiis.exe 与特定版本的 .net framework 相关联,管理员必须使用适当版本的 aspnet_regiis.exe,为 asp.net 应用程序重新配置脚本映射。aspnet_regiis.exe 仅将 asp.net 应用程序的脚本映射重新配置为与此工具关联的 asp.net isapi 版本。 此工具也可用来显示所有已安装的 asp.net 版本的状态、注册关联的 asp.net 版本、创建客户端脚本目录以及执行其他配置操作。
在 windows server 2003 系列产品上,在 iis 管理器中启用 asp.net
使用“配置您的服务器向导”,在运行 windows server 2003 的服务器上安装 asp.net
在任务栏上,单击“开始”按钮,然后单击“管理您的服务器”,在“管理您的服务器”窗口中单击“添加或移除角色”。 在“配置您的服务器向导”中,单击“下一步”,在“服务器角色”对话框中选中“应用程序服务器 (iis, asp.net)”,然后单击“下一步”。 在“应用程序服务器选项”对话框中,单击“启用 asp.net”复选框,单击“下一步”,再单击“下一步”。 如有必要,在 cd-rom 驱动器中插入 windows server 2003 安装 cd,然后单击“下一步”。 安装完成后,单击“完成”。 使用“添加/删除程序”对话框,在运行 windows server 2003 的服务器上安装 asp.net
在任务栏上,单击“开始”按钮,指向“控制面板”,然后单击“添加或删除程序”。 在“添加/删除程序”对话框中,单击“添加/删除 windows 组件”。 在“windows 组件向导”的“组件”框中,单击“应用程序服务器”复选框,然后单击“下一步”。 当“windows 组件向导”配置完 windows server 2003 后,单击“完成”。 在 windows server 2003 系列产品上,在 iis 管理器中启用 asp.net
在任务栏上单击“开始”按钮,然后单击“运行”。 在“运行”对话框的“打开”框中,输入“inetmgr”,然后单击“确定”。 在“iis 管理器”中,展开“本地计算机”,然后单击“web 服务扩展”。 在右窗格中,右击“asp.net”,然后单击“允许”。asp.net 的状态随即更改为“允许”。 当在运行 windows xp professional 或 windows 2000 server 的计算机上安装 visual studio .net 时,.net framework 和 asp.net 也会自动安装。如果要单独安装 asp.net 和 .net framework,可以从网上下载它们并安装到您的服务器上。下面的过程提供了具体的步骤。
在运行 windows xp professional 或 windows 2000 server 的计算机上下载和安装 asp.net
如有必要,安装并启动 iis。有关安装说明,请参见操作系统的文档。 在 msdn.microsoft.com/downloads/default.asp 上,展开“software development kits”(软件开发工具包),单击“microsoft .net framework sdk”,然后阅读页面上有关下载 sdk 的要求、说明和选项。 单击所需的下载选项,阅读最终用户许可协议,然后单击“yes”(是)。 在“文件下载”对话框中,选择保存下载文件,选择要将安装程序和自述文件下载到的文件夹,然后单击“保存”。 查看自述文件中任何最新的说明。 在下载文件所在的文件夹中,双击 .net framework 安装程序 setup.exe。 如果您已经安装并启用了 iis,安装了 asp.net 和 .net framework,部署了应用程序并请求了一个页,但是收到以下错误信息之一,这说明还没有为 web 站点或虚拟目录设置适当的权限:
对“c:/inetpub/wwwroot”目录的访问被拒绝。未能开始监视目录更改。 服务器无法访问应用程序目录“c:/inetpub/wwwroot/<虚拟目录名>/”。该目录不存在或者因安全设置而无法访问。 在根 web 站点或任何虚拟目录上,asp.net 需要 aspnet 帐户(aspnet_wp.exe 进程帐户)的读取、执行和列出权限。必须有这些设置,asp.net 才能访问内容文件和监视文件更改。请执行下面的步骤更正此问题。
在根 web 站点或虚拟目录上添加 aspnet 帐户的读取、执行和列出权限
在 windows 资源管理器中,浏览到包含根 web 站点(默认设置为:c:/inetpub/wwwroot)或虚拟目录的文件夹。 右击该文件夹,然后单击“属性”。 在“安全”选项卡上,单击“添加”。 输入 computername /aspnet(例如,在名为 webdev 的计算机上输入 webdev/aspnet),然后单击“确定”。 允许 aspnet 帐户的下列权限:读取并执行、列出文件夹内容、读取。 单击“确定”关闭“属性”对话框并保存更改。 注意 如果“everyone”(每个人)组或“用户”组能够读取根 web 站点或虚拟目录,则不需要执行这些步骤。
使用 iis 创建 asp.net web 应用程序根目录
本演练步骤向您说明如何使用 internet 信息服务 (iis) 将某个目录标记为应用程序根目录。此演练说明如何创建虚拟目录并将 c:/inetpub/wwwroot 目录设置为应用程序的根目录。
准备此次演练
创建一个新目录。此演练使用 c:/mywebapp 目录。 接下来,为 iis 打开 microsoft 管理控制台 (mmc),并使用下列过程之一创建一个虚拟目录。
使用 windows server 2003 系列产品创建虚拟目录
单击“开始”,指向“管理工具”,然后单击“internet 信息服务”。 展开“本地计算机”节点(这可能由您的计算机名称表示),展开“网站”,然后单击“默认网站”。 在“操作”菜单上单击“新建”,然后单击“虚拟目录”。 在“虚拟目录创建向导”中,单击“下一步”。 在“别名”文本框中输入您的应用程序名称,然后单击“下一步”。 输入为准备此演练而创建的物理目录 c:/mywebapp,然后单击“下一步”。 在“访问权限”页中,选中您的应用程序所需的访问权限,然后单击“下一步”。 单击“完成”。 使用 windows xp 创建虚拟目录
单击“开始”,然后单击“控制面板”。 如果尚未完成以上操作,单击“切换到传统型视图”。 双击“管理工具”,然后单击“internet 信息服务”。 展开“本地计算机”节点(这可能由您的计算机名称表示),展开“web 站点”,然后单击“默认 web 站点”。 在“操作”菜单上单击“新建”,然后单击“虚拟目录”。 在“虚拟目录创建向导”中,单击“下一步”。 在“别名”文本框中输入您的应用程序名称,然后单击“下一步”。 输入为准备此演练而创建的物理目录 c:/mywebapp,然后单击“下一步”。 在“访问权限”页中,选中您的应用程序所需的访问权限,然后单击“下一步”。 单击“完成”。 使用 windows 2000 创建虚拟目录
将 mmc 打开到“默认 web 站点”,过程同前。 在“默认 web 站点”中,单击要指定为应用程序根目录的子目录。在本例中,为 mywebapp。 右击要作为应用程序根目录的目录,然后单击“属性”。 在“虚拟目录”选项卡的“应用程序设置”节中,单击“创建”。 在“应用程序名”文本框中,输入应用程序的名称,然后单击“确定”。
asp.net 配置文件相关
可扩展的基础结构是 asp.net 配置系统的一大特色,该基础结构使您可以在最初部署 asp.net 应用程序时定义配置设置,以便可以随时添加或修改这些配置设置,同时对运作着的 web 应用程序和服务器产生的影响也将被减至最小。
asp.net 配置系统提供以下好处:
配置信息存储在基于 xml 的文本文件中。您可以使用任何标准的文本编辑器或 xml 分析器来创建和编辑 asp.net 配置文件。 多个配置文件(名称都是 web.config)可以出现在 asp.net web 应用程序服务器上的多个目录中。每个 web.config 文件都将配置设置应用于它自己的目录和它下面的所有子目录。子目录中的配置文件可以提供除从父目录继承的配置信息以外的配置信息,子目录配置设置可以重写或修改父目录中定义的设置。名为 systemroot/microsoft.net/framework/versionnumber/config/machine.config 的根配置文件提供整个 web 服务器的 asp.net 配置设置。 在运行时,asp.net 使用分层虚拟目录结构中 web.config 文件提供的配置信息为每个唯一的 url 资源计算一组配置设置。然后缓存结果配置设置,以供所有后面的对资源的请求使用。请注意,继承是由传入请求路径 (url) 定义的,而不是到磁盘上资源的文件系统路径(物理路径)定义的。 asp.net 检测对配置文件的更改并自动将新配置设置应用于受该更改影响的 web 资源。不需要重新启动服务器让更改生效。只要层次结构中的配置文件被更改,就将自动重新计算并重新缓存分层配置设置。 节例外。 asp.net 配置系统是可以扩展的。您可以定义新配置参数并编写配置节处理程序以对它们进行处理。 asp.net 通过配置 internet 信息服务 (iis) 防止对配置文件的直接浏览器访问来保护配置文件不受外部访问。向任何试图直接请求配置文件的浏览器返回 http 访问错误 403(禁止)。
asp.net 资源的配置信息包含在一组配置文件中,每个文件都名为 web.config。每个配置文件都包含 xml 标记和子标记的嵌套层次结构,这些标记带有指定配置设置的属性。因为这些标记必须是格式正确的 xml,所以标记、子标记和属性是区分大小写的。标记名和属性名是 camel 大小写形式的,这意味着标记名的第一个字符是小写的,任何后面连接单词的第一个字母是大写的。属性值是 pascal 大小写形式的,这意味着第一个字符是大写的,任何后面连接单词的第一个字母也是大写的。true 和 false 例外,它们总是小写的。
所有配置信息都驻留在 和 根 xml 标记之间。标记间的配置信息分为两个主区域:配置节处理程序声明区域和配置节设置区域。
您可以从 asp.net 应用程序使用 asp.net 公开的内部静态方法来访问公共配置设置。例如,若要读取 节 cookieless 属性的值,您可以使用下面的代码行。 [visual basic] dim nocookies as boolean = session.iscookieless [c#] bool nocookies = session.iscookieless; 可以使用 configurationsettings.appsettings 静态字符串集合来访问 web.config 文件顶级 节中存储的应用程序特定的设置。例如: [visual basic] dim dsn as string = configurationsettings.appsettings("dsn") [c#] string dsn = configurationsettings.appsettings["dsn"];
创建新的配置节
您可以用自己的 xml 配置标记扩展标准的 asp.net 配置设置集。若要完成该操作,您必须创建自己的配置节处理程序。该处理程序必须是一个实现 iconfigurationsectionhandler 接口的 .net framework 类。节处理程序解释并处理 web.config 文件特定部分中 xml 标记中定义的设置并根据配置设置返回适当的配置对象。处理程序类返回的配置对象可以是任何数据结构;它不限于任何基配置类或配置格式。 下面的示例定义 iconfigurationsectionhandler 接口。 [visual basic] namespace system.web.configuration public interface iconfigurationsectionhandler function create(parent as object, input as object, _ node as xmlnode) as object end interface end namespace [c#] namespace system.web.configuration { public interface iconfigurationsectionhandler { public object create(object parent, object input, xmlnode node); } } 您还可以定义自己的节,该节与 节使用相同的配置处理程序。例如:
您可以读取上面的示例中定义的新配置节的值,如下: [visual basic] dim config as namevaluecollection = configurationsettings.getconfig("mygroup/nestedgroup/mysection") response.write("the value of key_one is " & config("key_one") & " ") response.write("the value of key_two is " & config("key_two") & " ") [c#] namevaluecollection config = (namevaluecollection) configurationsettings.getconfig("mygroup/nestedgroup/mysection"); response.write("the value of key_one is " + config["key_one"] + " "); response.write("the value of key_two is " + config["key_two"] + " "); 部署 asp.net web 应用程序
部署 asp.net 应用程序非常简单。只需将所创建的应用程序文件从开发计算机复制到将承载应用程序的成品 web 服务器。可以使用 xcopy 命令行工具或喜欢的 ftp 应用程序,将文件从一个位置复制到另一个位置。
要部署希望在 web 应用程序间共享的程序集(比如包含自定义 asp.net 服务器控件的程序集),应将其部署到全局程序集缓存。
当您在 visual studio 中工作以创建 web 应用程序时,将需要在开发过程中访问一些资源,与这种访问相关的安全性存在一些特定的要求。这些要求不同于那些适用于应用程序用户的要求。设计时的安全性要求包括:
访问您的工作服务器、数据库服务器以及其他属于应用程序一部分的资源。 您选择了哪种 web 访问方法(您如何向 web 服务器传输数据)。 以完全受信任模式运行代码。 调试。 部署您的应用程序。 访问开发资源 为了在 visual studio 中创建和测试 web 应用程序,您必须能够访问运行 internet 信息服务 (iis) 的计算机。在此服务器(即开发服务器,它不同于您在其中部署应用程序的生产服务器)上,必须具有某些最低程度的特权,其中包括能够将文件写入服务器并运行这些文件。
web 服务器不必位于用于开发的同一台计算机上。但是,您必须至少在 web 服务器上安装 visual studio 的服务器组件,以使其支持应用程序的调试和部署。
对资源的设计时访问通常使用 windows nt 身份验证来处理,也就是说,作为开发人员,您使用自己的网络登录凭据来获取对所需资源的访问权。当 visual studio 安装完毕后,它会在服务器上创建一个名为“vs developers”(vs 开发人员)的组。该组具有在此计算机上开发 web 项目所必需的全部文件、共享和 iis 权限。(计算机管理员可以进一步为该组配置自定义权限,但这并不是必需的。)作为开发人员,您必须作为个人或您所属的另一个组的成员成为该组的成员。授予“vs developers”(vs 开发人员)组的特定权限有:
访问 web 服务器计算机的 wwwroot 目录的权限。 创建、修改和执行 web 目录中文件的权限。 调试远程 web 应用程序的权限。 安全说明 开发人员并不需要为使用某服务器进行开发而具有该服务器的管理员权限。(但是,所有开发人员都必须是“vs developers”(vs 开发人员)组的成员。)如果向开发人员授予不必要的管理员权限,则可能会给网络造成安全性风险。 如果您的应用程序需要访问进一步的资源,您必须同样地作为个人或作为组的成员建立对这些资源的访问权限。一个典型的示例就是 sql server。作为 web 应用程序开发人员,您将需要能够读取(并在可能时更新)应用程序所需的数据库表。对于某些方案,您还需要具有将存储过程写入服务器的权限;在其他方案中,则可能需要具有添加、改变或删除表的权限。
web 访问方法 visual studio 允许您以两种方式访问 web 应用程序项目:
使用文件共享访问(unc 访问)。在这种方式中,visual studio 使用基于 windows 的文件管理命令将文件复制到服务器上。 使用 frontpage 服务器扩展访问。在这种方式中,将使用 http 来传输文件。 文件共享访问要求您与 web 服务器位于同一域中。实际上,这意味着您只能在自己的网络上使用文件共享访问。
对比之下,frontpage 访问允许跨越防火墙创建和管理应用程序(只要防火墙传递 http 请求)。这样,就可以通过 internet 执行操作(当然也可以在您首选 http 访问的本地设置中执行操作)。
这两种访问方法仍然要求使用 visual studio 服务器组件、“vs developers”(vs 开发人员)组等正确配置服务器。同样,您仍然需要在服务器上具有足够的特权才能创建、编写和运行文件。
以完全受信任模式运行代码 当 visual studio 运行时,设计器在设计时运行的用户代码将始终以完全受信任模式运行。即使该代码最终部署到的环境将以限制较大的安全性来运行,情况也是如此。这包括两层含义:
可能会因为将不安全代码导入项目而给本地计算机带来安全性风险。由于 visual studio 以完全受信任模式运行,导入的代码会从 visual studio 继承其权限,而不安全的代码将作为安全代码来运行。只有在恶意用户创建具有破坏性的代码段(例如自定义控件),而您随后因疏忽导入并运行该代码段的情况下,才需要注意这一问题。因此,在将代码导入项目时,务必要加倍小心。 您在 visual studio 中创建的应用程序可能会在部署后无法正确运行,因为该代码是在不同的安全性上下文中运行。
调试 调试要求您能够附加到在本地计算机(如果 web 服务器位于另一台计算机上,则为远程计算机)上运行的进程。调试要求您使用“管理员”特权运行。如果您是在远程计算机上进行调试,则必须在本地计算机上和远程计算机上都具有相应的权限。
当 visual studio 安装完毕后,它将创建一个名为“debugger users”(调试器用户)的组,该组具有进行调试所必需的权限。通过将开发人员和调试用户划分到不同的组,服务器管理员可以向一个经过精选的组授予调试所必需的特权,而不是向所有使用 visual studio 的用户授予这些特权。
asp.net 支持通过以下方法对用户进行身份验证。其中几种方法与 iis 身份验证相重叠。有关详细信息,请参见介绍 web 应用程序安全性。
身份验证类型 说明 匿名访问 用于未知用户将在其中发出请求的应用程序(通常是公共 web 应用程序)。与 iis 重叠。 基本和简要身份验证 (与 iis 重叠)在此方案中,将提示无凭据的用户提供用户名和密码。 windows 集成安全性 (ntlm) (与 iis 重叠)如果发出请求的用户已经在基于 windows 的网络中进行了身份验证,则在请求对资源的访问时,iis 就可以通过该用户的凭据。 证书身份验证 (与 iis 重叠)在此方案中,客户端具有已从第三方来源获取的证书(数字标识)。该证书通过请求传递到您的应用程序。 kerberos (与 iis 重叠)kerberos 身份验证协议定义客户端与称作密钥分发中心 (key distribution center, kdc) 的网络身份验证服务之间的交互。windows 2000 在每个域控制器上以身份验证服务的形式来实现一个 kdc。有关详细信息,请参见 kerberos 协议的基本概念。 forms 身份验证 如果需要对某用户进行身份验证,asp.net 则会将请求重定向到您指定的页。该页通常包含一个您要在其中获取用户名信息的窗体。(如需额外的安全性,可以使用 https 协议交换该窗体。)当应用程序获取窗体信息时,它可以对用户的凭据执行应用程序特定的检查。值得注意的是,身份验证进程在您的控制之下(与 iis 不同),这使您能够指定窗体的外观并选择存储用户信息的方式。 如果某用户成功通过身份验证,asp.net 将颁发一个包含特定标记的加密 cookie,该标记将为后继的访问标识该用户。
passport 身份验证 利用 asp.net,您可以检查那些具有从符合 microsoft passport 的应用程序中获取的凭据的用户。 如果某用户成功通过身份验证,asp.net 将颁发一个包含特定标记的加密 cookie,该标记将为后继的访问标识该用户。
在 asp.net 应用程序中,forms 身份验证通常是最佳的选择,因此它使您能够在很大程度上控制如何对用户进行身份验证并允许您将身份验证存储在一个标记中。
xml web services 安全性 xml web services 使用 asp.net 并作为 web 应用程序来运行,因此它们所参与的安全性模型与任何 asp.net 应用程序所参与的安全性模型都相同。例如,xml web services 可能配置为使用基本身份验证或 windows 集成安全性。
在设计时,当您尝试向 xml web services 添加引用(即请求 xml web services 的发现文档)时,该 xml web services 将按照它的配置来执行标准的 web 应用程序身份验证。例如,如果 xml web services 配置为使用基本身份验证,该服务将需要从发出请求的客户端获取用户名和密码。在 visual studio 中,通常使用添加 web 引用对话框来发现 xml web services。例如,如果 xml web services 使用的是基本身份验证,“添加 web 引用”对话框将提示您提供凭据。
如果您正在生成的应用程序包含对 xml web services 的调用,则需要确保在进行调用之前具有正确的凭据,否则该调用将会失败。在运行时,可以通过在调用 xml web services 的方法之前设置表示 xml web services 的客户端对象的 credentials 属性来向 xml web services 传递凭据。
由于其他 asp.net 安全性选项可能不够灵活,xml web services 可以实现一种凭据信息在 soap 标头中进行传递的自定义身份验证解决方案。在该解决方案中,凭据在客户端和服务器之间交换的消息的可选部分进行传递。然后可以编写一个自定义的 httpmodule(实现 ihttpmodule 接口),它可以侦听标头中的信息并调用您的身份验证代码。
与其他 asp.net 应用程序相同,xml web services 可能会实现基于特定角色的授权,以限制对应用程序特定部分的访问。
经常进行备份,并将备份存放在安全的场所。 将您的 web 服务器计算机放置在安全的场所,这样,未经授权的用户就无法使用它、关闭它、带走它,等等。 使用 windows ntfs 文件系统,不使用 fat32。ntfs 的安全性比 fat32 高得多。有关详细信息,请参见 windows 文档。 使用不易破解的密码,确保 web 服务器计算机和同一网络上的所有计算机的安全。 确保 iis 的安全。有关详细信息,请参见 microsoft 安全性 web 站点 (http://microsoft.com/technet/treeview/default.asp?url=/technet/security/bestprac/mcswebbp.asp) 上的文章“管理 windows iis web 服务的安全性”。
关闭不使用的端口并关闭不使用的服务。 运行监视通信量的病毒检查程序。 建立并实施以下策略:禁止用户将其密码记在容易找到的位置。 使用防火墙。有关建议,请参见 microsoft 安全性站点上的“核对清单:安装防火墙”。(http://microsoft.com/security/articles/firewall.asp) 了解和安装来自 microsoft 和其他供应商的最新安全修补程序。例如,microsoft 安全站点 (www.microsoft.com/security) 中有所有 microsoft 产品的最新安全公告的列表。其他供应商也有类似站点。 使用 windows 事件日志记录,并且经常检查这些日志,以查找可疑活动。这样的活动包括:反复尝试登录您的系统,以及向您的 web 服务器发出数量巨大的请求。
在具有最少实用特权的上下文中运行应用程序。默认情况下,在 iis 5 版中,asp.net 应用程序运行在名为 aspnet 的本地用户的上下文中。在 windows server 2003 上的 iis 6 上,此帐户称为 network service。有关详细信息,请参见 asp.net 进程标识和配置 asp.net 进程标识。 不要在系统用户(管理员)的上下文中运行应用程序。 设置应用程序所需的所有资源上的权限(acl 或访问控制列表)。使用最少的容许设置。例如,如果在您的应用程序中是可行的,则将文件设置为只读。有关详细信息,请参见 windows 文档。 将您的 web 应用程序的文件保存在应用程序根目录下的一个文件夹中。不要让用户指定在应用程序中进行文件访问的路径。这样有助于防止用户访问服务器的根目录。 不要使不需要调试 visual studio 应用程序的任何人成为“debugger users”(调试器用户)组的成员。此组具有比“vs developers”(vs 开发人员)组更高的特权。有关详细信息,请参见添加调试器用户和配置 dcom。
了解您的用户
在许多应用程序中,用户有可能不必提供凭据即可访问站点。如果是这样,则您的应用程序通过在预定义用户的上下文中运行即可访问资源。默认情况下,此上下文是 web 服务器计算机上的本地 aspnet 用户(windows 2000 或 windows xp)或 network service 用户 (windows server 2003)。
若要仅允许已授权用户进行访问,请遵循以下准则:
如果您的应用程序是 intranet 应用程序,则将其配置为使用 windows 集成安全性。这样,用户的登录凭据就可以用于访问资源。 如果您需要从用户收集凭据,则使用其中的一种 asp.net 身份验证策略。有关更多信息,请参见 asp.net 身份验证。有关示例,请参见简单 forms 身份验证。
如果您的应用程序在浏览器和服务器之间传输敏感信息,请考虑使用安全套接字层 (ssl)。有关如何实现 ssl 的详细信息,请参见 microsoft 知识库文章 q307267“how to:在 windows 2000 中使用安全套接字层来保障 xml web services 的安全”(how to: secure xml web services with secure socket layer in windows 2000)。 如果您必须存储秘密,即使是以您认为人们将无法看到它的形式(如在服务器代码中)进行保存,也不要将它保存在 web 页中。 如果您对信息加密,请不要创建您自己的加密算法。请使用 windows 数据保护 api (dpapi)。
关闭或释放您使用的任何资源。例如,在使用完毕后,始终关闭数据连接和数据读取器,而且始终关闭文件。 使用错误处理(例如,try-catch)。包含 finally 块,以便万一失败就可以在其中释放资源。 将 iis 配置为使用进程调节,这样可以防止应用程序消耗过多的 cpu 时间。有关详细信息,请参见技术文章“使用 internet information server 5.0 的新功能:第 1 部分”。 在使用或存储用户输入之前,测试它的大小限制。 对数据库查询的大小加以限制以确保安全。例如,在 web 窗体页中显示查询结果之前,检查包含的记录数是否不合理。 如果文件上载是您的应用程序的一部分,则对它们的大小加以限制。您可以使用类似如下的语法在 web.config 文件中设置限制(其中 maxrequestlength 值以千字节为单位):
在解决方案资源管理器中,选择 webdeploy 项目。在“项目”菜单上选择“安装”。 注意 必须在计算机上具有安装权限才能运行安装程序,而且必须具有 iis(internet 信息服务)权限才能安装到 web 服务器。 若要访问已部署的 web 应用程序,请启动 internet explorer,然后键入 url localhost/xyz。注意,当在 web 页上单击该按钮时,该按钮上的文本将更改为“abc”。 将应用程序部署到另一台计算机上的 web 服务器
在 windows 资源管理器中,定位到项目目录并查找所生成的安装程序。默认路径是 /documents and settings/yourloginname/ my documents/visual studio projects/webapp1/webdeploy/project configuration/webdeploy.msi。默认“项目配置”是“调试”。 将该目录中的 webdeploy.msi 文件以及其他所有文件和子目录复制到 web 服务器计算机上。 在 web 服务器计算机上,双击 setup.exe 文件运行安装程序。 注意 必须在计算机上具有安装权限才能运行安装程序,而且必须具有 iis 权限才能安装到 web 服务器。 若要访问已部署的 web 应用程序,请启动 internet explorer,并键入 url [computername]/xyz。注意,当在 web 页上单击该按钮时,该按钮上的文本将更改为“abc”。 卸载应用程序
使用业务对象在业务组件中封装逻辑是实际应用程序的基本组成部分,不论是基于 web 的应用程序还是任何其他应用程序。在 asp.net 中,业务对象是多层 web 应用程序(如包含数据访问层或通用应用程序规则层的应用程序)的生成块。本节说明如何编写一些简单的组件并将它们包括在应用程序的 web 窗体页中。 应用程序 /bin 目录 使用 web 应用程序组件的 com 模型时遇到的问题是,这些组件必须注册(通常是使用 regsvr32 工具)后才能在传统的 asp 应用程序中使用。对这些类型的应用程序进行远程管理通常是不可能的,因为注册工具必须在服务器上以本地方式运行。更为棘手的是,这些组件经应用程序加载后便在磁盘上保持锁定状态,必须使整个 web 服务器停止运行后才能替换或移除这些组件。 为尝试解决这些问题,asp.net 允许将组件放置在已知目录中并在运行时自动找到它们。该已知目录总是命名为 /bin,并紧位于应用程序根目录(一个由 internet 信息服务 (iis) 定义的虚拟目录)下。这样做的好处是组件无需注册便可在 asp.net framework 应用程序中使用——只需将组件复制到 /bin 目录或执行 ftp 文件传输便可部署组件。 除提供零注册方法部署复杂组件外,asp.net 不要求这些组件在运行时在磁盘上保持锁定状态。asp.net 在后台复制 /bin 中的程序集并加载这些 shadow 副本。原始组件即使在 web 服务器仍在运行时也可以替换,而对 /bin 目录的更改则由运行时自动获取。当检测到更改时,asp.net 允许完成当前执行的请求,并将所有新传入的请求定向到使用新组件的应用程序。 导入业务对象 从最基本的层面看,业务组件只是一个类,可以从导入它的 web 窗体页为其创建实例。下面的示例定义一个简单的 helloworld 类。该类有一个公共构造函数(当初次创建该类的实例时执行此构造函数)、一个称为 firstname 的 string 属性和一个通过 firstname 属性值打印问候语的 sayhello 方法。
using system; using system.text;
namespace helloworld { public class helloobj { private string _name;
public helloobj() { _name = null;
}
public string firstname { get { return _name; } set { _name = value; } }
public string sayhello() { stringbuilder sb = new stringbuilder("hello "); if (_name != null) sb.append(_name); else sb.append("world");
sb.append("!"); return sb.tostring(); } } }
imports system imports system.text
namespace helloworld public class helloobj private _name as string
public sub new mybase.new() _name = nothing end sub
public property firstname as string get return(_name) end get set _name = value end set end property
public function sayhello() as string dim sb as new stringbuilder("hello ") if (_name <> nothing) then sb.append(_name) else sb.append("world") end if sb.append("!") return(sb.tostring()) end function end class end namespace
import system; import system.text;
package helloworld { public class helloobj { private var _name:string;
public function helloobj() { _name = null;
}
public function get firstname() : string { return _name; } public function set firstname(value:string) : void { _name = value; }
public function sayhello() : string { sb:stringbuilder = new stringbuilder("hello "); if (_name != null) sb.append(_name); else sb.append("world");
该组件现在可供应用程序中任何需要使用它的 web 窗体页使用。下面的 helloobj.aspx 示例阐释了此功能。
简单的两层 web 窗体页外部组件的典型用途是执行数据访问。它简化了页中的代码,增强了可读性并将用户界面 (ui) 逻辑与系统功能分开。下面的实例阐释了简单的两层 web 窗体页,它使用数据访问组件检索产品信息。
a simple two-tier webform
select a category:
using system.reflection;
[assembly: assemblytitle("")] [assembly: assemblydescription("a quickstart tutorial assembly")] [assembly: assemblyconfiguration("")] [assembly: assemblycompany("microsoft corporation")] [assembly: assemblyproduct("microsoft quickstart tutorials")] [assembly: assemblycopyright(" microsoft corporation. all rights reserved.")] [assembly: assemblytrademark("")] [assembly: assemblyculture("")] [assembly: assemblyversion("1.1.*")]
namespace datalayer { using system; using system.data; using system.data.sqlclient;
public class dataobj { private string _connstr;
public dataobj() { _connstr = null; }
public dataobj(string connstr) { _connstr = connstr; }
public string connectionstring { get { return _connstr; } set { _connstr = value; } }
public dataview getcategories() { sqlconnection myconnection = new sqlconnection(_connstr); sqldataadapter mycommand = new sqldataadapter("select distinct categoryname from categories", myconnection);
dataset ds = new dataset(); try { mycommand.fill(ds, "categories");
public dataview getproductsforcategory(string category) { sqlconnection myconnection = new sqlconnection(_connstr); sqldataadapter mycommand = new sqldataadapter("select productname, imagepath, unitprice, c.categoryid from products p, categories c where c.categoryname='" + category + "' and p.categoryid = c.categoryid", myconnection);
dataset ds = new dataset(); try { mycommand.fill(ds, "products");
简单的三层 web 窗体页三层应用程序模型扩展了两层方案,在用户界面和数据访问逻辑之间包含了业务规则。此模型允许用户界面开发人员处理更高抽象化的级别,而不是通过低级数据访问组件 api 直接操作数据。中间业务组件通常强制业务规则并确保遵守数据库的关系和主键约束。下面的示例使用中间组件基于客户端输入的两位供应商 id 计算折扣。
a simple three-tier web form
select a category:
preferred customer id:
customer id must be two numeric digits
using system.reflection;
[assembly: assemblytitle("")] [assembly: assemblydescription("a quickstart tutorial assembly")] [assembly: assemblyconfiguration("")] [assembly: assemblycompany("microsoft corporation")] [assembly: assemblyproduct("microsoft quickstart tutorials")] [assembly: assemblycopyright(" microsoft corporation. all rights reserved.")] [assembly: assemblytrademark("")] [assembly: assemblyculture("")] [assembly: assemblyversion("1.1.*")]
namespace businesslayer { using system; using system.data; using system.data.sqlclient; using datalayer;
public class busobj { private dataobj data;
public busobj() { data = new dataobj("server=(local)//netsdk;database=grocertogo;integrated security=sspi"); }
public dataview getcategories() { return data.getcategories(); }
public dataview getproductsforcategory(string category, int customerid) { dataview view = data.getproductsforcategory(category);
[assembly: assemblytitle("")] [assembly: assemblydescription("a quickstart tutorial assembly")] [assembly: assemblyconfiguration("")] [assembly: assemblycompany("microsoft corporation")] [assembly: assemblyproduct("microsoft quickstart tutorials")] [assembly: assemblycopyright(" microsoft corporation. all rights reserved.")] [assembly: assemblytrademark("")] [assembly: assemblyculture("")] [assembly: assemblyversion("1.1.*")]
namespace datalayer { using system; using system.data; using system.data.sqlclient;
public class dataobj { private string _connstr;
public dataobj() { _connstr = null; }
public dataobj(string connstr) { _connstr = connstr; }
public string connectionstring { get { return _connstr; } set { _connstr = value; } }
public dataview getcategories() { sqlconnection myconnection = new sqlconnection(_connstr); sqldataadapter mycommand = new sqldataadapter("select distinct categoryname from categories", myconnection);
dataset ds = new dataset(); try { mycommand.fill(ds, "categories");
public dataview getproductsforcategory(string category) { sqlconnection myconnection = new sqlconnection(_connstr); sqldataadapter mycommand = new sqldataadapter("select productname, imagepath, unitprice, c.categoryid from products p, categories c where c.categoryname='" + category + "' and p.categoryid = c.categoryid", myconnection);
dataset ds = new dataset(); try { mycommand.fill(ds, "products");
class test { public static void main() { // create an instance of streamwriter to write text to a file. // the using statement also closes the streamwriter. using (streamwriter sw = new streamwriter("testfile.txt")) { // add some text to the file. sw.write("this is the "); sw.writeline("header for the file."); sw.writeline("-------------------"); // arbitrary objects can also be written to the file. sw.write("the date is: "); sw.writeline(datetime.now); } } } using system; using system.io; public class texttofile { private const string file_name = "myfile.txt"; public static void main(string[] args) { if (file.exists(file_name)) { console.writeline("{0} already exists.", file_name); return; } streamwriter sr = file.createtext(file_name); sr.writeline ("this is my file."); sr.writeline ("i can write ints {0} or floats {1}, and so on.", 1, 4.2); sr.close(); } }
从文件读取文本using system; using system.io;
class test { public static void main() { try { // create an instance of streamreader to read from a file. // the using statement also closes the streamreader. using (streamreader sr = new streamreader("testfile.txt")) { string line; // read and display lines from the file until the end of // the file is reached. while ((line = sr.readline()) != null) { console.writeline(line); } } } catch (exception e) { // let the user know what went wrong. console.writeline("the file could not be read:"); console.writeline(e.message); } } } using system; using system.io; public class textfromfile { private const string file_name = "myfile.txt"; public static void main(string[] args) { if (!file.exists(file_name)) { console.writeline("{0} does not exist.", file_name); return; } streamreader sr = file.opentext(file_name); string input; while ((input=sr.readline())!=null) { console.writeline(input); } console.writeline ("the end of the stream has been reached."); sr.close(); } }
class test { public static void main() { string path = @"c:/temp/mytest.txt"; // this text is added only once to the file. if (!file.exists(path)) { // create a file to write to. using (streamwriter sw = file.createtext(path)) { sw.writeline("hello"); sw.writeline("and"); sw.writeline("welcome"); } }
// this text is always added, making the file longer over time // if it is not deleted. using (streamwriter sw = file.appendtext(path)) { sw.writeline("this"); sw.writeline("is extra"); sw.writeline("text"); }
// open the file to read from. using (streamreader sr = file.opentext(path)) { string s = ""; while ((s = sr.readline()) != null) { console.writeline(s); } } } }
using system; using system.io;
class test {
public static void main() { fileinfo fi = new fileinfo(@"c:/temp/mytest.txt");
// this text is added only once to the file. if (!fi.exists) { //create a file to write to. using (streamwriter sw = fi.createtext()) { sw.writeline("hello"); sw.writeline("and"); sw.writeline("welcome"); } }
// this text will always be added, making the file longer over time // if it is not deleted. using (streamwriter sw = fi.appendtext()) { sw.writeline("this"); sw.writeline("is extra"); sw.writeline("text"); }
//open the file to read from. using (streamreader sr = fi.opentext()) { string s = ""; while ((s = sr.readline()) != null) { console.writeline(s); } } } } ===================================== using system; using system.io;
public class appendtexttest { public static void main() { // create a reference to a file, which might or might not exist. // if it does not exist, it is not yet created. fileinfo fi = new fileinfo("temp.txt"); // create a writer, ready to add entries to the file. streamwriter sw = fi.appendtext(); sw.writeline("add as many lines as you like..."); sw.writeline("add another line to the output..."); sw.flush(); sw.close(); // get the information out of the file and display it. // remember that the file might have other lines if it already existed. streamreader sr = new streamreader(fi.openread()); while (sr.peek() != -1) console.writeline( sr.readline() ); } } 移动一个文件将指定文件移到新位置,并提供指定新文件名的选项。public static void move(string sourcefilename,string destfilename);参数sourcefilename 要移动的文件的名称。 destfilename 文件的新路径。 using system; using system.io;
class test { public static void main() { string path = @"c:/temp/mytest.txt"; string path2 = path + "temp"; try { if (!file.exists(path)) { // this statement ensures that the file is created, // but the handle is not kept. using (filestream fs = file.create(path)) {} }
// ensure that the target does not exist. if (file.exists(path2)) file.delete(path2);
// move the file. file.move(path, path2); console.writeline("{0} was moved to {1}.", path, path2);
// see if the original exists now. if (file.exists(path)) { console.writeline("the original file still exists, which is unexpected."); } else { console.writeline("the original file no longer exists, which is expected."); }
将一个文件移动至另一位置并重命名该文件。using system; using system.io;
public class movetotest { public static void main() { // create a reference to a file, which might or might not exist. // if it does not exist, it is not yet created. fileinfo fi = new fileinfo("temp.txt"); // create a writer, ready to add entries to the file. streamwriter sw = fi.appendtext(); sw.writeline("add as many lines as you like..."); sw.writeline("add another line to the output..."); sw.flush(); sw.close(); // ensure that the target file does not exist, since this is disallowed. if (file.exists("newtemp.txt")) file.delete("newtemp.txt"); // move this file to another file. fi.moveto("newtemp.txt"); // get the information out of the new file and display it. streamreader sr = new streamreader( fi.openread() ); console.writeline("{0}this is the information in the new file '{1}':", environment.newline, fi.name); while (sr.peek() != -1) console.writeline( sr.readline() ); } }