当前位置: 首页 > 图文教程 > 网络编程 > ASP.NET > 使用更精简的代码保证ASP.NET应用程序的安全

ASP.NET
ASP.NET开发:简化应用程序的开发支持Web标准
asp.net XMLHttpRequest实现用户注册前的验证
asp.net 页面间传值方法小结
asp.net url重写浅谈
asp.net 验证码生成和刷新及验证
C#精髓 GridView72大绝技 学习gridview的朋友必看
实例说明asp.net中的简单角色权限控制
asp.net网站开发包wq.dll打包下载
js与ASP.NET 中文乱码问题
asp.net checkbox 动态绑定id GridView删除提示
asp.net TextBox回车触发事件 图片在img显示
asp.net 脏字典过滤问题 用正则表达式来过滤脏数据
asp.NET 脏字过滤算法
asp.NET 脏字过滤算法 修改版
asp.net sql 数据库处理函数命令
asp.net Javascript 的几种写法与提示
ASP.NET MVC学习笔记
asp.net 中国身份证号码验证代码 非正则
Asp.net中使用Sqlite数据库的方法
asp.net 中文字符串提交乱码的解决方法

使用更精简的代码保证ASP.NET应用程序的安全


出处:互联网   整理: 软晨网(RuanChen.com)   发布: 2009-08-14   浏览: 94 ::
收藏到网摘: n/a

图15:通过在“Properties”(属性)窗口中设置“BodyFilename”和“BodyFormat”属性,可以提供电子邮件的HTML模板。

在示例中,我使用自己的SMTP服务器测试了该功能,而您将在web.config文件中看到SMTP设置的这个示例:

<smtpMailserverName="smtp.mysmtpserver.com"serverPort="25"></smtpMail>

这是一个奇妙的功能,为处理密码管理提供了快速的安全方案。但对于大型站点,您很可能希望进一步了解组件的体系结构。例如,您希望确保生成电子邮件的组件具有可伸缩性,您还可能想编写一些代码来控制如何通过电子邮件向成员发送密码:也许是提供对显示解密密码的已过期Web页面的链接。

按角色过滤内容

大多数应用程序依赖角色来控制对资源的访问、信息显示方式和可允许的活动。此前,我创建过许多用户,并使用安全管理工具来为其指定角色。如果是在ASP.NET的以前版本中使用这些角色,我就得编写代码,以从已验证的用户的凭据存储中手动检索角色。LoginView控件通过配置的成员身份提供程序(或者是成员身份API)与这些角色进行交互,并支持为任何有效角色提供内容模板。

假定我将把一组仅能由管理员访问的管理页面添加到应用程序。如果向标题中一个“Admin”(管理)菜单项,我很可能希望其仅对管理员显示。为了实现这一点,我将把另一个登录视图控件添加到菜单界面。LoginView控件的某个属性(可通过“Properties”[属性]窗口访问)支持通过对话框界面将角色列表添加到“RoleGroups”(角色组)集合:

图16:“RoleGroupCollectionEditor”(角色组集合编辑器)要求手动输入角色。您也可以为角色编组,这样多个组可以共享同样的模板界面。

在“Design”(设计)视图中,LoginView控件现在将角色列表显示为模板选项:

图17:“HTML”视图将更新,显示您为每个角色所设计的所有模板。

从以下HTML源文件中可以看出,“Admin”(管理)和“Member”(成员)角色使用了新的内容模板。在验证以前仍将使用<anonymoustemplate>,但验证以后,将使用与某个用户角色匹配的第一个模板。如果未找到匹配项,默认使用<loggedintemplate>设置。

<asp:loginviewid="lvMenu"runat="server"><anonymoustemplate><asp:loginstatusid="anonLoginStatus"runat="server"></asp:loginstatus></anonymoustemplate><rolegroups><asp:rolegrouproles="Admin"><contenttemplate><tr><tdclass="OtherTabs"><asp:hyperlinkid="adminHome"runat="server"navigateurl="~/default.aspx">Home</asp:hyperlink>&nbsp;|&nbsp;</td><tdclass="OtherTabs"><asp:hyperlinkid="adminAbout"runat="server"navigateurl="~/about.aspx">About</asp:hyperlink>&nbsp;|&nbsp;</td><tdclass="OtherTabs"><asp:hyperlinkid="adminAdmin"runat="server"navigateurl="~/admin/manageMembers.aspx">Admin</asp:hyperlink>&nbsp;|&nbsp;</td><tdclass="OtherTabs"><asp:loginstatusid="adminLoginStatus"runat="server"></asp:loginstatus></td></tr></contenttemplate></asp:rolegroup><asp:rolegrouproles="Member"><contenttemplate><tr><tdclass="OtherTabs"><asp:hyperlinkid="memberHome"runat="server"navigateurl="~/default.aspx">Home</asp:hyperlink>&nbsp;|&nbsp;</td><tdclass="OtherTabs"><asp:hyperlinkid="memberAbout"runat="server"navigateurl="~/about.aspx">About</asp:hyperlink>&nbsp;|&nbsp;</td><tdclass="OtherTabs"><asp:loginstatusid="memberLoginStatus"runat="server"></asp:loginstatus></td></tr></contenttemplate></asp:rolegroup></rolegroups><loggedintemplate><tr><tdclass="OtherTabs"><asp:hyperlinkid="authHome"runat="server"navigateurl="~/default.aspx">Home</asp:hyperlink>&nbsp;|&nbsp;</td><tdclass="OtherTabs"><asp:hyperlinkid="authAbout"runat="server"navigateurl="~/about.aspx">About</asp:hyperlink>&nbsp;|&nbsp;</td><tdclass="OtherTabs"><asp:loginstatusid="authLoginStatus"runat="server"></asp:loginstatus></td></tr></loggedintemplate></asp:loginview>

将按照显示的顺序分析这些模板,并将第一个匹配的角色用作该登录控件的内容。这意味着必须仔细地为角色安排适当的顺序。我的示例程序的结果是将新的“Admin”(管理)菜单项限制为只对分配了管理角色的用户显示。

我们还可以指定<authorization>规则拒绝或允许特定的角色,从而实现使用角色来控制对其他资源的访问。可以使用<location>标记在web.config文件的应用程序级别实现这一点,或是将web.config文件添加到受保护的子目录。我在示例程序的/admin目录下放置了以下<authorization>设置,只允许那些指定为管理角色的用户访问:

<authorization><allowroles="Admin"/><denyusers="*"/></authorization>

现在,可以创建一些管理页面来管理成员,并根据ASP.NETWhidbey提供的成员身份API来编写代码。

成员身份和角色提供程序

至此,我所显示的大多数内容都是基于使用新的安全控件。但是,有一些基础组件允许我们直接管理用户和角色。这些组件提供了从数据库访问层抽象而来的层。为了进行演示,我将在/admin目录下创建一个新的内容页面(manageMembers.aspx)。该页面将显示电子通讯成员的列表,并提供了一个中心界面,用于添加、编辑或删除电子通讯成员。

我将“DataView”(数据视图)控件拖放到页面中,目的是使用用户列表填充此控件。Page_Load事件包含了利用内部成员身份对象检索所有用户的代码。新的Membership类的方法和属性提供了对默认创建的成员身份数据库的直接访问。例如,GetAllUsers()返回了应用程序的MembershipUser对象的集合。以下代码将返回的集合转换为可以绑定到DataView控件的格式(用于Alpha版本的解决方案,因为该版本中不能绑定集合):

MembershipUserCollectionmembers=Membership.GetAllUsers();ArrayListarr=newArrayList();foreach(MembershipUsermemberinmembers){arr.Add(member);}GridView1.DataSource=arr;GridView1.DataBind();

在该页面中,用户可以添加、编辑或从列表删除成员。删除链接需仅执行以下代码行:

Membership.Provider.DeleteUser(user);

添加和编辑成员由所创建的另一个新页面(newMembers.aspx)来处理。添加新成员时,该页面收集要添加到成员数据库的新成员的必需信息。就我的电子通讯而言,我将收集新成员的电子邮件地址和密码,仅此而已。但是,数据库支持一个用户名和一个电子邮件地址,所以我同时使用电子邮件地址来填充这两个字段。此外,我将收集新用户的角色选择。这意味着我必须编写代码,以动态列出应用程序中所有可用的角色。

Page_Load事件包含用于加载可用角色的代码。我将使用“Repeater”控件来动态构建一个复选框列表(与安全配置向导中的列表类似)。

//从newMember.aspx<asp:repeaterrunat="server"id="roleRepeater"><itemtemplate><asp:checkboxrunat="server"id="chkRole"text='<%#Container.DataItem.ToString()%>'checked="<%#m_theUser==null?false:Roles.IsUserInRole(m_theUser.Username,Container.DataItem.ToString())%>"/><br/></itemtemplate></asp:repeater>//从newMember.aspx.csroleRepeater.DataSource=Roles.GetAllRoles();roleRepeater.DataBind();

所生成的输入页面如下所示:

图18:您也可以为自己的成员管理进程添加重设密码、更改密码和密码问题及答案功能。

我必须手动设计该页面,但其中添加新用户所需的代码很少,因为可以再次使用成员身份组件:

Membership.CreateUser(email.Text,pw.Text,email.Text);

我们需要多编写几行代码,以从Repeater控件中提取角色选择,然后用该用户的提取结果填充角色表。同样,访问角色数据库是很容易的,这次使用的是“Roles”(角色)组件:

string[]users={email.Text};string[]addRoles=newstring[roleRepeater.Items.Count];string[]remRoles=newstring[roleRepeater.Items.Count];intaddIndex=0;intremIndex=0;foreach(RepeaterItemitminroleRepeater.Items){CheckBoxc=(CheckBox)itm.FindControl("chkRole");stringrole=c.Text;if(c.Checked&&!Roles.IsUserInRole(users[0],role))addRoles[addIndex++]=role;elseif(!c.Checked&&Roles.IsUserInRole(users[0],role))remRoles[remIndex++]=role;}if(addIndex>0){string[]theRoles=newstring[addIndex];Array.Copy(addRoles,0,theRoles,0,addIndex);Roles.Provider.AddUsersToRoles(users,theRoles);}if(remIndex>0){string[]theRoles=newstring[remIndex];Array.Copy(remRoles,0,theRoles,0,remIndex);Roles.Provider.RemoveUsersFromRoles(users,theRoles);}

同一newMembers.aspx页面可以用于编辑用户,查询字符串则用于指示当前的模式。在编辑模式中,Page_Load将使用用户信息和当前角色填充界面。同样,Membership类提供了查找特定用户记录和更新已更改用户记录的方法。如果我使用纯文本密码,并且在成员身份提供程序配置设置中启用了密码检索,那么以下代码将更新用户的密码更改:

MembershipUseruser=Membership.GetUser(email.Text);user.ChangePassword(user.GetPassword(),this.pw.Text);

默认情况下,GetPassword()将失败,因为对所添加的安全性安装了每个成员身份提供程序的machine.config设置。此外,散列密码是提供程序的默认、也是推荐的方式。因为散列密码不可检索,所以您将必须从用户界面收集用户的旧密码,以调用ChangePassword()函数。

但是,这个程序是可扩展的吗?

至此,我一直侧重于如何才能更轻松地实现验证和基于角色的访问控制。虽然这些已封装的功能可以满足您百分之八十的需求,您还可以非常容易地扩展该模型。

数据库创建

例如,可以直接通过安全配置向导将数据库创建过程扩展为支持SQLServer数据库,或是另一个自定义的Access数据库。如果在向导的步骤中选择创建一个新数据库,您将看到如下的示例图:

图19:安全向导将在您所选择的本地或远程数据库中创建默认的一组成员身份管理表。

您可以在应用程序的SQLServer数据库中直接创建成员身份表,而不是使用无法进行任何扩展的Access数据库。这是我认为有进步的地方。也许您的祖母无法知道完成该步骤所需的数据库名称和凭据,但您的开发小组肯定知道。系统将封装所创建的表,以匹配成员身份和角色提供程序(这两者用于实现上述功能)的需要。但如果计划编译自己的提供程序,您可以使用其他表设计,并跳过这一步。

身份管理

您可能从我们编写的、用于构建某些管理功能的代码中发现,对凭据存储的访问由成员身份和角色提供程序进行处理。System.Web.Security命名空间现在包括新的SqlMembershipProvider和AccessMembershipProvider组件,以管理各自对默认凭据表的数据访问需求。默认情况下,machine.config文件的<membership>元素包括两个提供程序,分别用于SQLServer数据库和Access数据库。<providers>节可用于添加或删除提供程序,这允许您在应用程序级别删除这些默认的提供程序,并配置自己的提供程序。从以下设置中可以看到,有一些与向导创建的表结构直接相关的预定义设置,包括密码加密设置、密码重设和检索设置、密码问题及答案的要求设置,以及电子邮件字段输入内容的唯一性设置。其中的每一项都由各自的默认成员身份(或数据库)提供程序来强制执行,这使您可以使用多种方法使用已封装的表结构,而不是通过创建自己的表和提供程序。您也可以替代各个提供程序所用的连接字符串。

<membershipdefaultProvider="AspNetAccessProvider"userIsOnlineTimeWindow="15"><providers><addname="AspNetSqlProvider"type="System.Web.Security.SqlMembershipProvider,System.Web,Version=1.2.3400.0,Culture=neutral,PublicKeyToken=b03f5f7f11d50a3a"connectionStringName="LocalSqlServer"enablePasswordRetrieval="false"enablePasswordReset="true"requiresQuestionAndAnswer="false"applicationName="/"requiresUniqueEmail="false"passwordFormat="Hashed"description="从本地MicrosoftSQLServer数据库中存储和检索成员身份数据"/><addname="AspNetAccessProvider"type="System.Web.Security.AccessMembershipProvider,System.Web,Version=1.2.3400.0,Culture=neutral,PublicKeyToken=b03f5f7f11d50a3a"connectionStringName="AccessFileName"enablePasswordRetrieval="false"enablePasswordReset="true"requiresQuestionAndAnswer="false"applicationName="/"requiresUniqueEmail="false"passwordFormat="Hashed"description="从本地MicrosoftAccess数据库中存储和检索成员身份数据"/></providers></membership>

实际上,还有另一个成员身份提供程序组件System.Web.Security.ADMembershipProvider。该组件对ActiveDirectory存储执行上述同样的活动,但目前这只是内部功能。

<roleManager>节中的配置设置可以控制使用何种数据存储来访问相关的角色信息。默认情况下有三种配置设置:SqlRoleProvider、AccessRoleProvider和WindowsTokenRoleProvider。这些组件用于处理用户所有的角色管理。同样,将为SQLServer数据库和Access数据库创建一组默认表,WindowsTokenRoleProvider调用未托管的代码来访问为操作系统凭据存储而定义的角色。

<roleManagerenabled="false"cacheRolesInCookie="true"cookieName=".ASPXROLES"cookieTimeout="30"cookiePath="/"cookieRequireSSL="false"cookieSlidingExpiration="true"cookieProtection="All"defaultProvider="AspNetAccessProvider"><providers><addname="AspNetSqlProvider"type="System.Web.Security.SqlRoleProvider,System.Web,Version=1.2.3400.0,Culture=neutral,PublicKeyToken=b03f5f7f11d50a3a"connectionStringName="LocalSqlServer"applicationName="/"description="从本地MicrosoftSQLServer数据库中存储和检索角色数据"/><addname="WindowsToken"type="System.Web.Security.WindowsTokenRoleProvider,System.Web,Version=1.2.3400.0,Culture=neutral,PublicKeyToken=b03f5f7f11d50a3a"description="从请求的Windows已验证令牌检索角色数据"/><addname="AspNetAccessProvider"type="System.Web.Security.AccessRoleProvider,System.Web,Version=1.2.3400.0,Culture=neutral,PublicKeyToken=b03f5f7f11d50a3a"connectionStringName="AccessFileName"applicationName="/"description="从本地MicrosoftAccess数据库文件中存储和检索角色数据"/></providers></roleManager>

虽然此提供程序模型主要旨在简化Web应用程序的表单验证,但也可用于创建并管理用户和角色的任意验证方案,还可执行通用活动,如密码重设、密码加密和用户验证。

小结

ASP.NETWhidbey中的新组件和体系结构功能令人赞叹不已。新功能真正让人欣赏之处在于,您可以轻易地把各种功能组合在一起,构建成一个完整的应用程序,此外,对于需要具有可伸缩性的企业级应用程序,您可以非常容易地扩展这些功能。安全性也有了显著的加强。使用新模型使我们可以和使用XML配置文件开发可怜的“演示代码”说再见(这些XML配置文件保存未加密的凭据,并增加了服务器的文件访问负载)。当截至日期临近时,我们经常冒险发行演示代码。为什么不在第一次就开发出正确的代码呢?现在唯一欠缺的是心灵感应设备驱动程序,可以将我的想法实时转换成代码。