ASP.NET 2.0 中的新增安全功能

发布日期: 8/26/2004 | 更新日期: 8/26/2004

Stephen Walther

Microsoft Corporation

适用于:

Microsoft ASP.NET 2.0

Microsoft ASP.NET framework

Microsoft SQL Server

Microsoft Visual Studio .NET

摘要:ASP.NET 2.0 包含一些新增功能,以使确保 ASP.NET 应用程序的安全性变得比以前更加容易。了解如何使用新增的控件、工具和 API 来控制对页的访问,并且使存储有关用户的信息变得更加容易。

*
本页内容
安全性和提供程序模型 安全性和提供程序模型
使用 Web 站点管理工具来配置安全性 使用 Web 站点管理工具来配置安全性
使用 Login 控件来创建标准安全页 使用 Login 控件来创建标准安全页
直接使用 Membership API 直接使用 Membership API
小结 小结

ASP.NET 2.0 是在 ASP.NET 1.x 的基础之上构建的,使您能够更加容易地创建和管理用户,以及对 Web 应用程序中的页进行密码保护。新的框架包含用于处理身份验证和授权的新增功能,能够同时满足 Web 站点管理员和开发人员的需要。

Web 站点管理员可以利用新的 Web 站点管理工具来创建新的用户和角色,以及控制对 Web 应用程序中页面的访问。Web 站点管理工具是一组预先编写的 ASP.NET 页,不具备编程技巧的用户可以使用它们来配置 Web 应用程序。

开发人员可以利用新增的 Login 控件,以便快速地在 Web 应用程序中生成与安全性相关的页面。例如,开发人员可以简单地通过将 Login 控件拖到 .aspx 页上来创建登录页。通过利用 Login 控件,开发人员可以生成登录页、注册页或密码恢复页,而无须编写任何代码。

最后,ASP.NET 2.0 框架包含新增的安全性相关功能,这些功能能够满足高级开发人员的需要。新增的 Membership API 是一组类,这些类包含用于创建和检索有关应用程序用户的信息的方法。此外,新的框架包含能够使处理自定义用户角色变得更加容易的类。

安全性和提供程序模型

对于 ASP.NET 2.0 框架,您感受到的最大变化是安全性非常有效。使用新的框架,您在启用表单身份验证之后,可以立即针对用户数据库来注册和验证用户,而无须生成任何数据库表或者编写任何代码。这是因为 ASP.NET 2.0 框架使用提供程序模型来实现安全性。

提供程序模型(在整个 ASP.NET 2.0 框架中使用)为您提供了插入组件(它们为您的应用程序实现不同的服务)的标准方法。ASP.NET 2.0 框架使用两种类型的提供程序来实现安全性:成员身份提供程序和角色提供程序。成员身份提供程序用于存储用户名和密码,而角色提供程序用于存储用户角色。

默认的成员身份提供程序是 AccessMembershipProvider。该提供程序在 Microsoft Access 数据库中存储用户名和密码。将在应用程序的 Data 文件夹中为您自动创建 Access 数据库(如果您意外删除了 Access 数据库,则当您下次尝试连接到该数据库时,将自动重新创建该数据库)。每当您创建新的 Web 应用程序时,Access 提供程序都将自动创建您在开始验证用户身份时需要的所有内容。

ASP.NET 2.0 框架附带了两个成员身份提供程序:默认的 AccessMembershipProvider 以及 SqlMembershipProvider。如果您希望在 Microsoft SQL Server 数据库中存储成员身份信息,则可以将您的应用程序配置为使用 SqlMembershipProvider,而无须重新编写任何应用程序代码(启用 SqlMembershipProvider 的步骤将在下一节讨论)。

您还可以创建自定义成员身份提供程序。例如,您可能希望在 XML 文件、FoxPro 数据库或 Oracle 数据库中存储成员身份信息。您甚至还可能希望实现通过 Web 服务检索成员身份信息的成员身份提供程序。如果您希望创建您自己的成员身份提供程序,您需要实现抽象类 MembershipProvider 的所有方法和属性(成员身份提供程序只是 MembershipProvider 基类的一个实例而已)。

使用 SqlMembershipProvider

如果您正在开发 Web 应用程序,或者正在开发准备供少数用户使用的应用程序,则使用 Access 数据库就已经很不错了。然而,如果您需要生成更为健壮的应用程序,您需要在具有更高可伸缩性的数据库(如 Microsoft SQL Server)中存储用户名和密码。

如果您希望在 Microsoft SQL Server 数据库而不是默认的 Microsoft Access 数据库中存储成员身份信息,则需要为您的应用程序修改默认的成员身份提供程序。下面的应用程序 Web.Config 文件将 AspNetSqlProvider 设置为默认的成员身份提供程序:

<configuration>
    <system.web>
        <membership defaultProvider="AspNetSqlProvider" />
    </system.web>
</configuration>

如果您不希望手动更新应用程序的 Web.Config 文件,您还可以通过使用 Web 站点管理工具(在下一节中介绍)或用于 Internet 信息服务的 ASP.NET Microsoft 管理控制台 (MMC) 管理单元来切换提供程序。这两个工具都为您提供了用于指定成员身份提供程序的用户友好界面。

与 Microsoft Access 提供程序不同,您必须首先创建必要的数据库表和存储过程,然后才能使用 SQL 提供程序。通过从命令行执行 aspnet_regsql 工具,您可以自动创建所有必需的 SQL Server 数据库对象(参见图 1)。默认情况下,该工具将在本地 SQL Server 实例上创建一个名为 aspnetdb 的新数据库。


1. 配置 SqlMembershipProvider

在您创建新的数据库以后,您需要确保该数据库可供您的 ASP.NET 应用程序访问。默认情况下,将使用集成安全性。因此,您需要确保 ASP.NET 帐户(对于 Windows Server 2003 而言,为 NT AUTHORITY\NETWORK SERVICE 帐户;对于 Windows 2000 或 XP 而言,为 ASPNET 帐户)具有访问 aspnetdb 数据库所需的权限。

配置成员身份提供程序属性

AccessMembershipProviderSqlMembershipProvider 都支持多个为提供程序所特有的属性:

applicationName — 如果您需要在同一台 Web 服务器上宿主多个应用程序,则可以使用该属性来隔离与不同应用程序相关联的用户。

connectionStringName — 在 Web 配置文件的 connectionStrings 节中定义的数据库连接字符串的名称。

description — 提供程序定义的说明。

enablePasswordReset — 值为 true 时,用户可以将其密码重置为随机生成的密码。

enablePasswordRetrieval — 值为 true 时,可以从成员身份提供程序中检索用户密码。

passwordFormat — 该属性可能具有三个值:ClearEncryptedHashed。当密码被哈希后,将无法从成员身份提供程序中检索原来的密码。

requiresQuestionAndAnswer — 值为 true 时,用户必须首先回答一个密码检索问题,然后才能重置或检索用户密码。

requiresUniqueEmail — 值为 true 时,必须将唯一的电子邮件地址与各个用户相关联。

可以使用这些成员身份提供程序属性来控制在数据库中存储和检索成员身份信息的方法。可以在应用程序的 Web 配置文件中更改这些属性的值。

例如,passwordFormat 属性确定了在数据库中存储密码的方式。您可以选择存储明文密码、加密密码或密码哈希值。出于安全原因,您可能希望在数据库中存储哈希值而不是实际的密码,以便在您的 Web 应用程序受到损害时,使黑客无法窃取实际的用户密码。

还要注意,您还可以通过设置 enablePasswordRetrieval 属性,允许或禁止从数据库中检索用户密码。同样,为了确保安全,您可能不希望允许用户检索他们的密码。

使用 Web 站点管理工具来配置安全性

ASP.NET 2.0 框架随附的 Web 站点管理工具使您可以完全通过 Web 页界面来配置 ASP.NET 应用程序(参见图 2)您可以使用 Web 站点管理工具来创建和管理用户和角色,以及控制对 Web 应用程序中文件夹和单个页面的访问(Web 站点管理工具还可以用来配置 Web 应用程序的若干其他方面)。


2. 使用 Web 站点管理工具

有多种导航到 Web 管理界面的方法。如果您要在 Visual Studio .NET 2005 内部生成 Web 应用程序,您可以通过从 Website 菜单下选择 ASP.NET Configuration 来打开 Web 站点管理工具。如果您要在 Visual Studio .NET 外部开发 Web 应用程序,您可以通过请求特殊的页 WebAdmin.axd 来直接导航到 Web 站点管理工具。例如,如果您的应用程序位于本地计算机上名为 MyWebApp 的虚拟目录中,则可以通过在 Web 浏览器中输入以下 URL,打开应用程序的 Web 站点管理工具。

http://localhost/MyWebApp/WebAdmin.axd

您还可以使用这后一种方法来访问已部署的应用程序的 Web 站点管理工具。

在幕后,Web 站点管理工具由一组 ASP.NET 页组成,这些页使用下一节所讨论的标准 Login 控件。这些页位于 inetpub\wwwroot\aspnet_webadmin 文件夹中。如果由于某种原因,这些文件被从服务器中意外删除,您可以通过执行 aspnet_regiis 工具来自动重新安装它们。还可以使用 aspnet_regiis 工具来控制对 Web 站点管理工具的访问。例如,可以使用 aspnet_regiis 工具将对 Web 站点管理工具的访问权限制到本地服务器。

使用 Login 控件来创建标准安全页

ASP.NET 2.0 包含一组与安全性有关的新控件,统称为 Login 控件。通过利用 Login 控件,您无须编写任何代码,就可以创建标准的注册、登录和密码恢复页。

您还可以使用 Login 控件来向用户显示不同的信息,具体取决于他们的角色和当前的身份验证状态。例如,通过 LoginView 控件可以定义不同的模板,将其显示给不同角色的成员。举例来说,可以使用该控件向 Administrators 角色的成员显示与向 Guests 角色成员显示的不同的信息。

在幕后,Login 控件充分利用了提供程序模型。如果您已经将应用程序配置为使用 AccessMembershipProvider,则 Login 控件将自动查询 Microsoft Access 数据库来检索成员身份信息。如果启用了 SqlMembershipProvider,则这些控件将使用已配置的 SQL Server 数据库。

在使用任何 Login 控件之前,您应该为您的应用程序启用表单身份验证。可以通过修改应用程序的 Web 配置文件或者通过使用 Web 站点管理工具来启用表单身份验证。

使用 Login 控件

Login 控件使您能够简单地通过向页中添加单个标记来创建标准登录页。

<asp:Login ID="Login1" Runat="server" />

Login 控件会自动生成标准登录界面。默认情况下,该界面不是非常漂亮,但您可以在 Visual Studio .NET 2005 中通过右键单击 Login 控件并选择 AutoFormat,快速地改善该控件的外观(参见图 3)。注意,您将获得标准的用户名和密码文本框、一个“remember me”复选框以及一个提交按钮。


3. 用 Login 控件创建登录界面

Login 控件不仅为您提供界面,而且能够实际工作!当您通过使用 Login 控件来提交您的用户名和密码时,您的凭据将自动被已配置的成员身份提供程序验证。

存在一系列与 Login 控件相关联的、数量惊人的属性。这些属性中的大多数都只是使您能够控制登录界面的外观的不同方面。例如,您可以使用各种 FailureText 属性来控制登录失败时显示的文本的内容和外观。此外,您还可以使用 CreateUserUrlPasswordRecoveryUrl 属性来创建指向注册页和密码恢复页的链接。

Login 控件附带的更为有价值的属性之一是 VisibleWhenLoggedIn 属性。该属性使您能够在已经对用户进行身份验证后自动隐藏 Login 控件。例如,您可能希望在 Web 应用程序中每个页的顶部包含 Login 控件。通过使用 VisibleWhenLoggedIn 属性,您可以在对用户进行身份验证后立即自动隐藏 Login 控件。

使用 CreateUserWizard 控件

CreateUserWizard 控件使您能够创建标准用户注册页。只是通过向页中添加以下标记,您就可以使新用户能够在您的 Web 站点注册。

 <asp:CreateUserWizard ID="CreateUserWizard1" Runat="server" />

CreateUserWizard 所生成界面的确切外观取决于应用程序的成员身份提供程序的设置。例如,仅当默认成员身份提供程序的 requiresQuestionAndAnswer 属性值为 true 时,密码问题和回答的文本框才会出现。图 4 中的注册页是在 CreateUserWizard 与默认的 AspNetAccessProvider 成员身份提供程序配合使用时生成的。


4. 用 CreateUserWizard 控件注册新用户

使用 CreateUserWizard 控件能够做的更为有趣的事情之一,是在用户已经完成所有注册步骤后自动发送注册电子邮件。例如,您可以发送电子邮件以感谢用户在您的 Web 站点注册。该电子邮件可以包含诸如用户的注册用户名和密码之类的信息。

通过向 CreateUserWizard 控件的 MailDefinition 属性赋值,可以配置由该控件发送的电子邮件。MailDefinition 属性代表了 MailDefinition 类的实例,该类包含定义电子邮件所需的全部属性。

例如,每当某个用户完成注册向导时,下面的 CreateUserWizard 控件都会将 RegistrationEmail.txt 文件的内容作为注册电子邮件的正文发送(CreateUserWizard 在发送电子邮件时,使用在 Web 配置文件的 <smtpMail> 邮件节中指定的电子邮件服务器)。

<asp:CreateUserWizard ID="CreateUserWizard1" Runat="server">
   <MailDefinition 
     BodyFileName="~/RegistrationEmail.txt" 
     From="[email protected]YourDomain.com"
     Subject="Thanks for registering!">
   </MailDefinition>
</asp:CreateUserWizard>

在 RegistrationEmail.txt 文件内部,您可以使用特殊的表达式(如 <% UserName %> 和 <% Password %>),以便替换电子邮件正文内新用户的注册用户名和密码。

在更为复杂的注册方案(您在向用户提供对 Web 应用程序的访问权之前,需要验证用户电子邮件地址的有效性)中,CreateUserWizard 控件的电子邮件功能也可能很有用。如果您启用 CreateUserWizard 控件的 AutoGeneratePassword 属性,则该控件将为用户随机生成密码。通过利用 CreateUserWizard 控件的电子邮件功能,您可以自动将随机生成的密码发送给用户。如果该用户随后使用已发送的密码来对 Web 应用程序进行身份验证,则您将知道用户肯定已经提供了有效的电子邮件地址。

使用 PasswordRecovery 控件

PasswordRecovery 控件使 Web 应用程序的用户能够请求其密码的电子邮件提醒(参见图 5)。像 CreateUserWizard 控件一样,您可以用 MailDefinition 属性来定义发送给用户的电子邮件的属性。


5. 用 PasswordRecovery 控件通过电子邮件发送密码

例如,以下标记向页中添加了一个 PasswordRecovery 控件,该控件从 [email protected] 电子邮件帐户发送电子邮件。

 <asp:PasswordRecovery ID="PasswordRecovery1" Runat="server">
   <MailDefinition 
      From="[email protected]" 
      Subject="Password Reminder">
   </MailDefinition>
 </asp:PasswordRecovery>

PasswordRecovery 控件具有不同的行为,具体取决于默认成员身份提供程序的配置。当 enablePasswordRetrieval 属性的值为 true 并且 passwordFormat 属性的值不为 Hashed 时,将在电子邮件中发送用户的原始明文密码。对于其他几乎每种属性值组合,都会首先将密码重置为随机生成的字符序列,然后再将其发送给用户。

使用 ChangePassword 控件

您可能猜到,ChangePassword 控件使用户能够更改他们的已注册密码。该控件可以呈现用于输入原始密码以及输入新密码的文本框(参见图 6)。


6. 用 ChangePassword 控件更改密码

CreateUserWizardPasswordRecovery 控件一样,ChangePassword 控件包括一个 MailDefinition 属性。如果将值赋给 MailDefinition 属性,则 ChangePassword 控件在密码成功更改时将自动向用户发送一个电子邮件。

以下标记声明了一个 ChangePassword 控件实例并设置了 MailDefinition 属性。

<asp:ChangePassword ID="ChangePassword1" Runat="server">
   <MailDefinition 
     BodyFileName="~/ChangePasswordEmail.txt" 
     From="[email protected]"
     Subject="Your Updated Password">
   </MailDefinition>
</asp:ChangePassword>

BodyFileName 属性包含特定文本文件(该文件包含被发送给用户的电子邮件的正文)的路径。在该文本文件内部,您可以使用特殊表达式(如 <% UserName %> 和 <% Password %>)在电子邮件中显示用户的注册用户名和已修改的密码。

使用 LoginName 和 LoginStatus 控件

LoginNameLoginStatus 控件使您能够显示有关某个用户的当前身份验证状态的信息。在某个用户登录到您的应用程序以后,LoginName 控件会显示该用户的注册用户名。如果用户尚未由表单身份验证进行身份验证,则 LoginName 控件不会显示任何信息。以下为在页上声明 LoginName 控件的方式。

<asp:LoginName ID="LoginName1" Runat="server" />

另一方面,LoginStatus 使用户能够登录到您的 Web 应用程序或者注销。该控件显示两个链接中的一个。如果用户尚未经过身份验证,则显示指向 Login.aspx 页的链接。如果用户已经进行了身份验证,则显示使该用户能够注销的链接。以下为声明 LoginStatus 控件的方式。

<asp:LoginStatus ID="LoginStatus1" Runat="server" />

使用 LoginView 控件

最后一个 Login 控件 — LoginView 控件使您能够根据当前用户的角色显示不同的内容。例如,许多 Web 站点根据用户是新用户还是注册用户来在其主页上显示不同的信息。新用户可以概览 Web 站点的用途,而注册用户可以查看专门为他们定制的信息。

以下代码显示了如何使用 LoginView 控件向匿名用户显示与向经过身份验证的用户显示的不同的内容。

<asp:LoginView ID="LoginView1" Runat="server">
   <LoggedInTemplate>
     Welcome back <asp:LoginName ID="LoginName1" Runat="server" />
  </LoggedInTemplate>
  <AnonymousTemplate>
     Welcome to our Web site! If you were a registered user, 
     you could view some really interesting stuff right now!
  </AnonymousTemplate>
</asp:LoginView>

匿名用户可以查看 AnonymousTemplate 中包含的所有内容。另一方面,经过身份验证的用户可以查看 LoggedInTemplate 中包含的所有内容。

如果您为 Web 应用程序启用自定义角色,则可以使用 LoginView 控件显示只能由特定角色的成员查看的内容。例如,设想您使用 Web 站点管理工具创建了一个名为 Administrators 的新角色。在这种情况下,您可以使用 LoginView 控件显示只能由 Administrators 角色的成员查看的内容,如下所示。

<asp:LoginView ID="LoginView1" Runat="server">
   <RoleGroups>
     <asp:RoleGroup Roles="Administrators">
       <ContentTemplate>
         Secret stuff for administrators!
       </ContentTemplate>
     </asp:RoleGroup>
   </RoleGroups>
   <LoggedInTemplate>
     Welcome back <asp:LoginName ID="LoginName1" Runat="server" />
   </LoggedInTemplate>
   <AnonymousTemplate>
     Welcome to our Web site! If you were a registered user, 
     you could view some really interesting stuff right now!
  </AnonymousTemplate>
</asp:LoginView>

Administrators 角色的成员将能够查看与 Administrators RoleGroup 相关联的 <ContentTemplate> 中包含的任何内容。但是,Administrators 角色的成员将无法查看在 LoggedInTemplateAnonymous 模板内部声明的内容。即使有多个模板适用于您,您也只能查看单个模板的内容。

直接使用 Membership API

有时候,您需要对成员身份进行更高级别(比 Web 站点管理工具或 Login 控件提供的控制级别高)的控制。在这些情况下,您可以直接使用 Membership API。

Membership API 是通过 Membership 类公开的。Membership 类包含的方法使您能够完成以下工作:创建新用户;更改密码;搜索与特定条件匹配的用户;等等。在幕后,Login 控件使用这些方法与已配置的成员身份提供程序进行交互。

下面列出了 Membership 类的一些比较重要的方法:

CreateUser — 使您能够创建新用户。

DeleteUser — 使您能够删除现有用户。

FindUsersByEmail — 使您能够检索与某个特定的电子邮件地址匹配的用户集合。

FindUsersByName — 使您能够检索与某个特定的用户名匹配的用户集合。

GeneratePassword — 使您能够生成随机密码。

GetAllUsers — 使您能够检索成员身份提供程序中存储的所有用户。

GetNumberOfUsersOnline — 使您能够返回当前正在访问 Web 应用程序的用户数。

GetUser — 使您能够检索与当前用户相关联的成员身份信息,或者使您能够检索与某个已经提供用户名的用户相关联的成员身份信息。

GetUsernameByEmail — 使您能够检索具有某个特定电子邮件地址的用户的用户名。

UpdateUser — 使您能够更新特定用户的信息。

ValidateUser — 使您能够根据成员身份提供程序对用户进行身份验证。

这些方法非常强大。例如,通过利用 CreateUser 方法,您可以用一行代码创建新用户。

// C#
Membership.CreateUser( "Ruth", "Secret" );
' VB.NET
Membership.CreateUser( "Ruth", "Secret" )

该语句创建了一个名为 Ruth 的新用户,该用户具有密码 Secret

有几个 Membership 方法(如 GetAllUsersFindUsersByName)返回 MembershipUser 对象的集合。MembershipUser 类表示有关特定用户的信息。该类具有下列属性:

Comment — 表示与该用户相关联的任意备注。

CreationDate — 表示该用户的创建日期。

Email — 表示该用户的电子邮件地址。

IsApproved — 表示该用户是否已经被批准。

IsOnline — 表示该用户当前是否正在访问 Web 应用程序。

LastActivityDate — 表示该用户上次访问 Web 应用程序的日期。

LastLoginDate — 表示上次对该用户进行身份验证的日期。

LastPasswordChangedDate — 表示上次更改用户密码的日期。

PasswordQuestion — 表示与密码问题和回答配合使用的密码问题。

Provider — 表示成员身份提供程序。

Username — 表示用户名。

MembershipUser 类还包含下列方法:

ChangePassword — 使您能够更改用户的密码。

ChangePasswordQuestionAndAnswer — 使您能够更改密码问题和回答。

GetPassword — 使您能够检索用户的密码。

ResetPassword — 使您能够重置用户的密码。

您可以使用 MemberUser 类的属性来显示有关 Web 应用程序的用户的信息。例如,清单 1 中的 ASP.NET 页显示了 GridView 控件中每个用户的用户名、电子邮件地址、上次活动日期和联机状态(参见图 7)。


7. 用 Membership API 显示成员身份信息

清单 1. DisplayMembers.aspx (C#)

<%@ Page Language="C#" %>
<script runat="server">

void Page_Load() {
    grdUsers.DataSource = Membership.GetAllUsers();
    grdUsers.DataBind();
}

</script>

<html>
<head runat="server">
    <title>Display Members</title>
</head>
<body>
<form runat="Server">

<asp:GridView ID="grdUsers" AutoGenerateColumns="false" Runat="Server">
<Columns>
    <asp:BoundField HeaderText="Username" DataField="Username" />
    <asp:BoundField HeaderText="Email" DataField="Email" />
    <asp:BoundField HeaderText="Last Activity Date" 
     DataField="LastActivityDate" DataFormatString="{0:d}" />
    <asp:CheckBoxField HeaderText="Is Online" DataField="IsOnline" />
</Columns>
</asp:GridView>

</form>
</body>
</html>

清单 1. DisplayMembers.aspx (Visual Basic .NET)

<%@ Page Language="VB" %>
<script runat="server">

    Sub Page_Load()
        grdUsers.DataSource = Membership.GetAllUsers()
        grdUsers.DataBind()
    End Sub

</script>

<html>
<head id="Head1" runat="server">
    <title>Display Members</title>
</head>
<body>
<form id="Form1" runat="Server">

<asp:GridView ID="grdUsers" AutoGenerateColumns="false" Runat="Server">
<Columns>
    <asp:BoundField HeaderText="Username" DataField="Username" />
    <asp:BoundField HeaderText="Email" DataField="Email" />
    <asp:BoundField HeaderText="Last Activity Date" 
     DataField="LastActivityDate" DataFormatString="{0:d}" />
    <asp:CheckBoxField HeaderText="Is Online" DataField="IsOnline" />
</Columns>
</asp:GridView>

</form>
</body>
</html>

用户在 Page_Load 方法内部绑定到 GridView 控件。通过使用 Membership 类的 GetAllUsers 方法检索用户列表。

小结

ASP.NET 2.0 框架包含重要的新增安全性功能,能够同时满足 Web 站点管理员和开发人员的需要。新的 Web 站点管理工具使您能够完全通过 Web 表单界面来创建和管理用户。新的 Login 控件使您能够生成标准的 Web 站点安全页,而无须编写一行代码。最后,Membership API 为您提供了一组功能强大的方法,用于通过代码操纵成员身份信息。

相关书籍

ASP.NET Unleashed

A First Look at ASP.NET V. 2.0

ASP.NET 2.0 Revealed

关于作者

Stephen Walther 撰写了有关 ASP.NET 的最畅销著作 — ASP.NET Unleashed他还曾是 ASP.NET Community Starter Kit(一个由 Microsoft 生产的 ASP.NET 示例应用程序)的架构师和首席开发人员。他已经通过他的公司 Superexpert (http://www.superexpert.com) 为美国各地的公司(包括 NASA 和 Microsoft)提供了 ASP.NET 培训。

转到原英文页面