<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0">
<channel>
<title><![CDATA[宇文祺]]></title> 
<link>http://www.ywqi.com/index.php</link> 
<description><![CDATA[当思考成为习惯，成功将随之而至]]></description> 
<language>zh-cn</language> 
<copyright><![CDATA[宇文祺]]></copyright>
<item>
<link>http://www.ywqi.com/read.php/225.htm</link>
<title><![CDATA[以编程方式控制 ASP.NET 菜单 ]]></title> 
<author>bgaidu &lt;admin@yourname.com&gt;</author>
<category><![CDATA[asp.net]]></category>
<pubDate>Tue, 06 Jan 2009 08:03:00 +0000</pubDate> 
<guid>http://www.ywqi.com/read.php/225.htm</guid> 
<description>
<![CDATA[ 
	创建网站<br/>如果已经在 Visual Web Developer 中创建了一个网站（例如，按照演练：在 Visual Web Developer 中创建基本网页中的步骤），则可以使用该网站并转到本演练后面部分的“创建站点地图文件”。否则，按照下面的步骤创建一个新的网站和网页。<br/><br/>创建文件系统网站<br/>打开 Visual Web Developer。<br/><br/>在“文件”菜单上单击“新建网站”。<br/><br/>出现“新建网站”对话框。 <br/><br/>在“Visual Studio 已安装的模板”之下单击“ASP.NET 网站”。<br/><br/>在“位置”框中输入要保存网站页面的文件夹的名称。 <br/><br/>例如，键入文件夹名“C:&#92;WebSites”。 <br/><br/>在“语言”列表中，单击您想使用的编程语言。<br/><br/>单击“确定”。<br/><br/>Visual Web Developer 创建该文件夹和一个名为 Default.aspx 的新页。<br/><br/>创建站点地图文件<br/>两个菜单都从站点的 Web.sitemap 文件派生其内容。两个菜单都使用您创建的 SiteMapDataSource 对象确保每个菜单分别显示站点地图的相应部分。<br/><br/>创建 Web.sitemap 文件<br/>在解决方案资源管理器中，右击网站的名称，然后单击“添加新项”。<br/><br/>在“添加新项”对话框中，选择“站点地图”，然后单击“添加”。<br/><br/>将下面的 XML 代码添加到新文件。该 XML 表示菜单选择的层次结构。siteMapNode 元素是嵌套的，并且每个节点都是包含子项的菜单项，有些子项还有自己的子项。Home 节点下面有三个二级节点：Hardware、Software 和 Support。这些二级节点下面是各个子类别。<br/><br/>&nbsp;&nbsp;复制代码 <br/><?xml version="1.0" encoding="utf-8" ?><br/><siteMap><br/> <siteMapNode title="Home"><br/>&nbsp;&nbsp;<siteMapNode title="Products"><br/>&nbsp;&nbsp; <siteMapNode title="Hardware" url="Default.aspx?node=hardware"><br/>&nbsp;&nbsp;&nbsp;&nbsp;<siteMapNode title="Mouse"/><br/>&nbsp;&nbsp;&nbsp;&nbsp;<siteMapNode title="Keyboard"/><br/>&nbsp;&nbsp;&nbsp;&nbsp;<siteMapNode title="NetCard"/><br/>&nbsp;&nbsp;&nbsp;&nbsp;<siteMapNode title="Monitor"/><br/>&nbsp;&nbsp;&nbsp;&nbsp;<siteMapNode title="PC"/><br/>&nbsp;&nbsp; </siteMapNode><br/>&nbsp;&nbsp; <siteMapNode title="Software" url="Default.aspx?node=software"><br/>&nbsp;&nbsp;&nbsp;&nbsp;<siteMapNode title="Spreadsheet"/><br/>&nbsp;&nbsp;&nbsp;&nbsp;<siteMapNode title="Word Processor"/><br/>&nbsp;&nbsp;&nbsp;&nbsp;<siteMapNode title="Presentation"/><br/>&nbsp;&nbsp;&nbsp;&nbsp;<siteMapNode title="Mail"/><br/>&nbsp;&nbsp;&nbsp;&nbsp;<siteMapNode title="Games"/><br/>&nbsp;&nbsp; </siteMapNode><br/>&nbsp;&nbsp; <siteMapNode title="Books" url="Default.aspx?node=books"><br/>&nbsp;&nbsp;&nbsp;&nbsp;<siteMapNode title="Programming"/><br/>&nbsp;&nbsp;&nbsp;&nbsp;<siteMapNode title="Debugging"/><br/>&nbsp;&nbsp;&nbsp;&nbsp;<siteMapNode title="Testing"/><br/>&nbsp;&nbsp;&nbsp;&nbsp;<siteMapNode title="Web Apps"/><br/>&nbsp;&nbsp;&nbsp;&nbsp;<siteMapNode title="WinForm Apps"/><br/>&nbsp;&nbsp; </siteMapNode><br/>&nbsp;&nbsp;</siteMapNode><br/>&nbsp;&nbsp;<siteMapNode title="Services"><br/>&nbsp;&nbsp; <siteMapNode title="Consulting" url="Default.aspx?node=consulting"><br/>&nbsp;&nbsp;&nbsp;&nbsp;<siteMapNode title="Processes"/><br/>&nbsp;&nbsp;&nbsp;&nbsp;<siteMapNode title="Management"/><br/>&nbsp;&nbsp;&nbsp;&nbsp;<siteMapNode title="Recruiting"/><br/>&nbsp;&nbsp; </siteMapNode><br/>&nbsp;&nbsp; <siteMapNode title="Development" url="Default.aspx?node=development"><br/>&nbsp;&nbsp;&nbsp;&nbsp;<siteMapNode title="Web Apps"/><br/>&nbsp;&nbsp;&nbsp;&nbsp;<siteMapNode title="Enterprise Apps"/><br/>&nbsp;&nbsp;&nbsp;&nbsp;<siteMapNode title="Database"/><br/>&nbsp;&nbsp; </siteMapNode><br/>&nbsp;&nbsp;</siteMapNode><br/>&nbsp;&nbsp;<siteMapNode title="Support"><br/>&nbsp;&nbsp; <siteMapNode title="Drivers" url="Default.aspx?node=drivers"><br/>&nbsp;&nbsp;&nbsp;&nbsp;<siteMapNode title="Audio"/><br/>&nbsp;&nbsp;&nbsp;&nbsp;<siteMapNode title="Network"/><br/>&nbsp;&nbsp;&nbsp;&nbsp;<siteMapNode title="Printer"/><br/>&nbsp;&nbsp;&nbsp;&nbsp;<siteMapNode title="Modem"/><br/>&nbsp;&nbsp; </siteMapNode><br/>&nbsp;&nbsp; <siteMapNode title="Manuals" url="Default.aspx?node=manuals"><br/>&nbsp;&nbsp;&nbsp;&nbsp;<siteMapNode title="Applications"/><br/>&nbsp;&nbsp;&nbsp;&nbsp;<siteMapNode title="Troubleshooting"/><br/>&nbsp;&nbsp;&nbsp;&nbsp;<siteMapNode title="Installation"/><br/>&nbsp;&nbsp;&nbsp;&nbsp;<siteMapNode title="Internet"/><br/>&nbsp;&nbsp; </siteMapNode><br/>&nbsp;&nbsp; <siteMapNode title="Updates" url="Default.aspx?node=updates"><br/>&nbsp;&nbsp;&nbsp;&nbsp;<siteMapNode title="Release 1"/><br/>&nbsp;&nbsp;&nbsp;&nbsp;<siteMapNode title="Game Package"/><br/>&nbsp;&nbsp; </siteMapNode><br/>&nbsp;&nbsp;</siteMapNode><br/> </siteMapNode><br/></siteMap><br/> <br/><br/>保存该文件。<br/><br/>创建第一个菜单<br/>第一个菜单显示一个单级别静态菜单项。它水平显示在页的顶部。<br/><br/>创建第一个菜单<br/>在解决方案资源管理器中，双击 Default.aspx 页将其打开。<br/><br/>切换到“设计”视图。<br/><br/>从“工具箱”中的“导航”控件组中，将一个“Menu”控件拖到该页上。<br/><br/>在“属性”窗口中，将“方向”属性设置为“水平”。<br/><br/>将“MaximumDynamicDisplayLevels”设置为 0。<br/><br/>这样可确保菜单的任何部分都不会以动态飞出方式出现。<br/><br/>确保将“StaticDisplayLevels”设置为 1。这样只允许在菜单中显示一个级别。<br/><br/>单击“Menu”控件上的智能标记。 <br/><br/>出现“菜单任务”对话框。<br/><br/>从“选择数据源”下拉菜单中选择“新建数据源”。<br/><br/>在“数据源配置向导”中，选择“站点地图”。<br/><br/>接受默认名称“SiteMapDataSource1”然后单击“确定”。<br/>配置第一个数据源<br/>由于第一个菜单仅显示单级别静态菜单项，需要配置其数据源才能显示 Web.sitemap 文件的相应部分。在本例中，需要配置的是二级元素：Products、Services 和 Support。<br/><br/>Menu 控件到 SiteMapDataSource 控件的默认绑定是为了使每个菜单项都成为一个导航链接。由于想要以编程方式控制另一个菜单的行为，因此使用自定义绑定，使这些菜单项不作为导航链接，而是引发回发以便能够更新第二个菜单。<br/><br/>配置第一个数据源<br/>在“设计”视图中查看 Default.aspx 页，然后单击“Menu”控件上的智能标记。 <br/><br/>出现“菜单任务”对话框。<br/><br/>单击“编辑 MenuItem DataBinding”。<br/><br/>在“菜单 DataBindings 编辑器”中的“可用数据绑定”下拉列表中，选择“SiteMapNode”然后单击“添加”。<br/><br/>在“数据绑定属性”下拉列表中，选择“TextField”，然后从下拉菜单中选择“标题”。单击“确定”。<br/><br/>选择“SiteMapDataSource”控件。 <br/><br/>在“属性”中，将“ShowStartingNode”设置为“False”。<br/><br/>创建第二个菜单<br/>第二个菜单是动态的，其数据源仅使用站点地图文件中由第一个菜单中选择的菜单项所确定的部分。和前面一样，希望该菜单静态显示其第一级别；但是，现在希望动态显示 Web.sitemap 文件的其余部分。<br/><br/>创建第二个菜单<br/>在“设计”视图中查看 Default.aspx 页，然后将另一个“Menu”控件拖到该页上第一个“Menu”控件的下面。<br/><br/>在“属性”中，将“方向”设置为“水平”。<br/><br/>单击第二个“Menu”控件上的智能标记。 <br/><br/>出现“菜单任务”对话框。 <br/><br/>从“选择数据源”下拉列表中选择“新建数据源”。<br/><br/>在“数据源配置向导”中，选择“站点地图”。<br/><br/>接受默认名称“SiteMapDataSource2”然后单击“确定”。<br/><br/>配置第二个数据源<br/>在本节中，将配置数据源以便仅选择 Web.sitemap 文件的特定节。为此，首先从第一个菜单的默认第一个类别开始，该类别是 Web.sitemap 文件中的 Products 节。然后使用“StartingNodeURL”属性来指示该文件中的某个特定 URL 属性。<br/><br/>配置第二个数据源的起点<br/>选择“SiteMapDataSource2”并将其“StartingNodeURL”属性设置为“Default.aspx?node=hardware”。<br/><br/>将“StartingNodeOffset”设置为 -1。 <br/><br/>将“ShowStartingNode”设置为“False”。<br/><br/>添加代码以协调菜单<br/>若要根据第一个菜单的状态控制第二个菜单，请捕捉第一个菜单的 MenuItemClick 事件，并在代码中指示站点地图文件的第二个菜单的视图。<br/><br/>在代码中协调菜单<br/>在“设计”视图中查看 Default.aspx 页，然后选择“Menu1”。<br/><br/>在“属性”中，将“MenuItemClick”事件设置为 Menu1_MenuItemClick。 <br/><br/>这样将在 Default.aspx 页的代码隐藏文件中为您创建一个名为 Menu1_MenuItemClick 的方法。<br/><br/>将下面突出显示的代码添加到该方法中。<br/><br/>Visual Basic&nbsp;&nbsp;复制代码 <br/>Protected Sub Menu1_MenuItemClick(ByVal sender As Object, _<br/>&nbsp;&nbsp;ByVal e As System.Web.UI.WebControls.MenuEventArgs) _<br/>&nbsp;&nbsp;Handles Menu1.MenuItemClick<br/>&nbsp;&nbsp;Select Case e.Item.Value&nbsp;&nbsp;&nbsp;&nbsp;Case "Products"&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SiteMapDataSource2.StartingNodeUrl = "Default.aspx?node=hardware"&nbsp;&nbsp;&nbsp;&nbsp;Case "Services"&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SiteMapDataSource2.StartingNodeUrl = "Default.aspx?node=consulting"&nbsp;&nbsp;&nbsp;&nbsp;Case "Support"&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SiteMapDataSource2.StartingNodeUrl = "Default.aspx?node=drivers"&nbsp;&nbsp;End Select<br/>End Sub<br/> <br/><br/><br/>C#&nbsp;&nbsp;复制代码 <br/>protected void Menu1_MenuItemClick(Object sender,&nbsp;&nbsp;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.Web.UI.WebControls.MenuEventArgs e)<br/>&#123;<br/>&nbsp;&nbsp;switch(e.Item.Value)&nbsp;&nbsp;&#123;&nbsp;&nbsp;&nbsp;&nbsp;case "Products":&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SiteMapDataSource2.StartingNodeUrl = "Default.aspx?node=hardware";&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return;&nbsp;&nbsp;&nbsp;&nbsp;case "Services":&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SiteMapDataSource2.StartingNodeUrl = "Default.aspx?node=consulting";&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return;&nbsp;&nbsp;&nbsp;&nbsp;case "Support":&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SiteMapDataSource2.StartingNodeUrl = "Default.aspx?node=drivers";&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return;&nbsp;&nbsp;&#125;<br/>&#125;<br/> <br/><br/>上面的代码捕捉“Menu1”的单击事件。使用该值确定第二个 Menu 控件的显示内容，而不是导航到任何位置。这可通过调整第二个 Menu 控件的 SiteMapDataSource 控件的 StartingNodeUrl 属性实现。<br/><br/>保存该文件。<br/><br/>测试菜单<br/>若要测试这两个菜单之间的交互，请单击第一个菜单上的项，观察第二个菜单如何相应改变。第二个菜单是动态的，它显示 Web.sitemap 文件的第三级别。<br/><br/>测试菜单<br/>按 Ctrl+F5 运行该页。<br/><br/>出现第一个菜单，同时第二个菜单显示 Web.sitemap 文件中“Products”下面的菜单项。<br/><br/>单击第一个菜单中的“Services”。<br/><br/>第二个菜单现在显示“Consulting”和“Development”动态菜单。<br/><br/>单击“Support”。<br/><br/>第二个菜单显示“Drivers”、“Manuals”和“Updates”动态菜单项。<br/>
]]>
</description>
</item><item>
<link>http://www.ywqi.com/read.php/224.htm</link>
<title><![CDATA[NET 基于角色安全性验证]]></title> 
<author>bgaidu &lt;admin@yourname.com&gt;</author>
<category><![CDATA[asp.net]]></category>
<pubDate>Tue, 06 Jan 2009 01:13:05 +0000</pubDate> 
<guid>http://www.ywqi.com/read.php/224.htm</guid> 
<description>
<![CDATA[ 
	[.NET 基于角色安全性验证] 之一：基础知识 <br/>.NET 基于角色安全性验证的核心是主体(Principal)和标识(Identity)对象，其中主体负责角色或者组的验证，标识对象封装有关正在验证的用户或实体的信息。角色安全性验证通过生成可供当前线程使用的主体信息来支持授权，其中主体用关联的标识进行构造。<br/><br/>public interface IPrincipal<br/>&#123;<br/> // Methods<br/> bool IsInRole(string role);<br/><br/> // Properties<br/> IIdentity Identity &#123; get; &#125;<br/>&#125;<br/><br/>public interface IIdentity<br/>&#123;<br/> // Properties<br/> string AuthenticationType &#123; get; &#125;<br/> bool IsAuthenticated &#123; get; &#125;<br/> string Name &#123; get; &#125;<br/>&#125;<br/><br/><br/>在 .NET Framework 中提供了两组 Principal/Identity 类型，分别是基于 Windows 操作系统账户的 WindowsPrincipal/WindowsIdentity，以及用来进行自定义验证的 GenericPrincipal/GenericIdentity。<br/><br/>主体(Principal)对象在应用程序域(AppDomain)中绑定到调用上下文(CallContext)对象，缺省情况下，应用程序域会自动创建采取默认安全策略的 GenericPrincipal/GenericIdentity对象，同时主体(Principal)对象引用从创建线程自动复制到新线程的调用上下文(CallContext)中。我们可以通过 Thread.CurrentPrincipal 获得缺省主体(Principal)和标识(Identity)对象信息。<br/><br/>Console.WriteLine(Thread.CurrentPrincipal);<br/>Console.WriteLine(Thread.CurrentPrincipal.Identity);<br/><br/><br/>接下来，我们使用 WindowsPrincipal/WindowsIdentity 做一些简单的验证，同时为了进一步理解基于角色安全性验证的用途。<br/><br/>static void Test()<br/>&#123;<br/>&nbsp;&nbsp;Console.WriteLine(&quot;Test...&quot;);<br/>&#125;<br/><br/>static void Main(string[] args)<br/>&#123;<br/>&nbsp;&nbsp;Test();<br/>&#125;<br/><br/><br/>我们修改上面这段代码，要求 Test() 方法必须是拥有管理员权限才能调用。<br/><br/>1. 我们使用 System.Security.Permissions.PrincipalPermissionAttribute 特性为 Test 方法加上角色验证标记，要求调用用户(Windows 操作系统登录用户)必须是 &quot;Administrators&quot; 组成员。<br/><br/>2. 在 Main 方法中设置线程的主体和标识对象。WindowsIdentity.GetCurrent() 用来获取当前登录用户的标识对象，如果登录用户属于 Adminstrators 组，则 Test() 方法正常调用，否则会触发 SecurityException 异常(我们可以使用 WindowsIdentity.GetAnonymous() 获取匿名用户标识对象来触发该异常)。<br/><br/>[PrincipalPermission(SecurityAction.Demand, Role = &quot;Administrators&quot;)]<br/>static void Test()<br/>&#123;<br/>&nbsp;&nbsp;Console.WriteLine(&quot;Test...&quot;);<br/>&#125;<br/><br/>static void Main(string[] args)<br/>&#123;<br/>&nbsp;&nbsp;Thread.CurrentPrincipal = new WindowsPrincipal(WindowsIdentity.GetCurrent());<br/>&nbsp;&nbsp;Test();<br/>&#125;<br/><br/><br/>我们还可以使用其他的方法来做到这一点。<br/><br/>1. 使用 PrincipalPermission 对象替换 PrincipalPermissionAttribute，我们就可以使用动态权限验证，可以将用户名或者角色参数写入配置文件中。<br/>2. 使用 AppDomain.CurrentDomain.SetThreadPrincipal 设置主体对象和 Thread.CurrentPrincipal 作用相同。我们还可以直接使用 AppDomain.CurrentDomain.SetPrincipalPolicy(PrincipalPolicy.WindowsPrincipal) 让系统自动使用当前登录用户名创建 WindowsPrincipal/WindowsIdentity。<br/><br/>static void Test()<br/>&#123;<br/>&nbsp;&nbsp;new PrincipalPermission(null, &quot;Administrators&quot;).Demand();<br/>&nbsp;&nbsp;Console.WriteLine(&quot;Test...&quot;);<br/>&#125;<br/><br/>static void Main(string[] args)<br/>&#123;<br/>&nbsp;&nbsp;AppDomain.CurrentDomain.SetThreadPrincipal(new WindowsPrincipal(WindowsIdentity.GetCurrent()));<br/>&nbsp;&nbsp;Test();<br/>&#125;<br/><br/><br/>或者<br/><br/>static void Test()<br/>&#123;<br/>&nbsp;&nbsp;new PrincipalPermission(null, &quot;Administrators&quot;).Demand();<br/>&nbsp;&nbsp;Console.WriteLine(&quot;Test...&quot;);<br/>&#125;<br/><br/>static void Main(string[] args)<br/>&#123;<br/>&nbsp;&nbsp;AppDomain.CurrentDomain.SetPrincipalPolicy(PrincipalPolicy.WindowsPrincipal);<br/>&nbsp;&nbsp;Test();<br/>&#125;<br/><br/><br/>上面的例子都是基于角色或者组的验证，当然我们还可以基于用户进行验证。<br/><br/>[PrincipalPermission(SecurityAction.Demand, )]<br/><br/>new PrincipalPermission(&quot;YUHEN&#92;&#92;q.yuhen&quot;, null).Demand();<br/><br/><br/>当然，我们还可以同时用 role 和 name。PrincipalPermission 对象比 PrincipalPermissionAttribute 更加灵活，我们可以使用它进行多个权限的并集运算，以及进行 xml 转换等。<br/><br/>下面是使用 GenericPrincipal/GenericIdentity 改写的代码。<br/><br/>//[PrincipalPermission(SecurityAction.Demand, , Role=&quot;admins&quot;)]<br/>static void Test()<br/>&#123;<br/>&nbsp;&nbsp;new PrincipalPermission(null, &quot;Administrators&quot;).Demand();<br/>&nbsp;&nbsp;Console.WriteLine(&quot;Test...&quot;);<br/>&#125;<br/><br/>static void Main(string[] args)<br/>&#123;<br/>&nbsp;&nbsp;// 创建自定义用户标识对象。<br/>&nbsp;&nbsp;GenericIdentity identity = new GenericIdentity(&quot;q.yuhen&quot;);<br/>&nbsp;&nbsp;<br/>&nbsp;&nbsp;// 创建主体对象，并指定所拥有的角色数组。<br/>&nbsp;&nbsp;string[] roles = new string[] &#123; &quot;admins&quot; &#125;;<br/>&nbsp;&nbsp;GenericPrincipal principal = new GenericPrincipal(identity, roles);<br/>&nbsp;&nbsp;<br/>&nbsp;&nbsp;// 设定主体。<br/>&nbsp;&nbsp;AppDomain.CurrentDomain.SetThreadPrincipal(principal);<br/>&nbsp;&nbsp;<br/>&nbsp;&nbsp;Test();<br/>&#125;<br/><br/><br/>除了使用上述方法外，某些时候我们并不希望触发 SecurityException 异常，我们希望能给出另外的执行策略，那么可以直接使用 Principal.InRole 以及 Identity.Name 了。<br/><br/>static void Test()<br/>&#123;<br/>&nbsp;&nbsp;if (Thread.CurrentPrincipal.IsInRole(&quot;admins&quot;) &amp;&amp; Thread.CurrentPrincipal.Identity.Name == &quot;q.yuhen&quot;)<br/>&nbsp;&nbsp;&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;Console.WriteLine(&quot;Test...&quot;);<br/>&nbsp;&nbsp;&#125;<br/>&nbsp;&nbsp;else<br/>&nbsp;&nbsp;&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;Console.WriteLine(&quot;您没有执行权限！&quot;);<br/>&nbsp;&nbsp;&#125;<br/>&#125;<br/><br/><br/>总结一下，基于角色的安全检查有三种方法。<br/><br/>1. 使用命令式安全检查。该方法主要是通过 PrincipalPermission.Demand() 方法进行。<br/>2. 使用声明性安全检查。通过 PrincipalPermissionAttribute 特性指定运行所需角色和用户名。<br/>3. 直接访问 Principal 对象。访问 Thread.CurrentPrincipal 属性获得当前主体和标识对象，并调用其相关方法和属性进行进一步验证。<br/><br/>有关细节可参考 MSDN 文档，角色验证主要用于 ASP.NET 环境，雨痕将在后续 Blog 中做进一步说明。<br/><br/>[.NET 基于角色安全性验证] 之二：ASP.NET Forms 身份验证流程分析<br/><br/>MSDN 中提及 FormsAuthenticationModule 在 Forms 身份验证中起到了关键作用，那么这背后究竟隐藏了什么？本分将简要分析 Forms 身份验证流程，以便让大家更加清楚地了解并使用它。<br/><br/>FormsAuthenticationModule 是一个 Http Module，Forms 身份验证通过 FormsAuthenticationModule 参与 ASP.NET 页的生命周期。它在网站应用启动时被初始化，并拦截访问请求，我们继续看看它的细节。<br/><br/>public void Init(HttpApplication app)<br/>&#123;<br/>&nbsp;&nbsp;// 绑定 HttpApplication 的两个事件，以此来拦截系统的访问请求。<br/>&nbsp;&nbsp;app.AuthenticateRequest += new EventHandler(this.OnEnter);<br/>&nbsp;&nbsp;app.EndRequest += new EventHandler(this.OnLeave);<br/>&#125;<br/><br/>private void OnEnter(object source, EventArgs eventArgs)<br/>&#123;<br/>&nbsp;&nbsp;if (!FormsAuthenticationModule._fAuthChecked &#124;&#124; FormsAuthenticationModule._fAuthRequired)<br/>&nbsp;&nbsp;&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;HttpApplication application1 = (HttpApplication)source;<br/>&nbsp;&nbsp;&nbsp;&nbsp;HttpContext context1 = application1.Context;<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;// 读取 Forms 认证的配置信息<br/>&nbsp;&nbsp;&nbsp;&nbsp;AuthenticationSection section1 = RuntimeConfig.GetAppConfig().Authentication;<br/>&nbsp;&nbsp;&nbsp;&nbsp;section1.ValidateAuthenticationMode();<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;// 确认 Forms 验证模式<br/>&nbsp;&nbsp;&nbsp;&nbsp;if (!FormsAuthenticationModule._fAuthChecked)<br/>&nbsp;&nbsp;&nbsp;&nbsp;&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;FormsAuthenticationModule._fAuthRequired = section1.Mode == AuthenticationMode.Forms;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;FormsAuthenticationModule._fAuthChecked = true;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&#125;<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;if (FormsAuthenticationModule._fAuthRequired)<br/>&nbsp;&nbsp;&nbsp;&nbsp;&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// 设置缺省参数<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (!this._fFormsInit)<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;FormsAuthentication.Initialize();<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;this._FormsName = section1.Forms.Name;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (this._FormsName == null)<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;this._FormsName = &quot;.ASPXAUTH&quot;;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#125;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;FormsAuthenticationModule.Trace(&quot;Forms name is: &quot; + this._FormsName);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;this._LoginUrl = section1.Forms.LoginUrl;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (this._LoginUrl == null)<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;this._LoginUrl = &quot;login.aspx&quot;;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#125;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;this._fFormsInit = true;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#125;<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// 调用验证核心方法<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;this.OnAuthenticate(new FormsAuthenticationEventArgs(context1));<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;CookielessHelperClass class1 = context1.CookielessHelper;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (AuthenticationConfig.AccessingLoginPage(context1, this._LoginUrl))<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;context1._skipAuthorization = true;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;class1.RedirectWithDetectionIfRequired(null, FormsAuthentication.CookieMode);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#125;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (!context1.SkipAuthorization)<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;context1._skipAuthorization = AssemblyResourceLoader.IsValidWebResourceRequest(context1);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#125;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&#125;<br/>&nbsp;&nbsp;&#125;<br/>&#125;<br/><br/>private void OnAuthenticate(FormsAuthenticationEventArgs e)<br/>&#123;<br/>&nbsp;&nbsp;HttpCookie cookie1 = null;<br/>&nbsp;&nbsp;if (this._eventHandler != null)<br/>&nbsp;&nbsp;&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;this._eventHandler(this, e);<br/>&nbsp;&nbsp;&#125;<br/><br/>&nbsp;&nbsp;// 检查 User 对象，以确认是否已通过验证。<br/>&nbsp;&nbsp;if ((e.Context.User != null) &#124;&#124; (e.User != null))<br/>&nbsp;&nbsp;&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;// 处理 Global.asax FormsAuthentication_OnAuthenticate 事件参数。<br/>&nbsp;&nbsp;&nbsp;&nbsp;if (e.Context.User == null)<br/>&nbsp;&nbsp;&nbsp;&nbsp;&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;e.Context._user = e.User;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&#125;<br/>&nbsp;&nbsp;&#125;<br/>&nbsp;&nbsp;else<br/>&nbsp;&nbsp;&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;FormsAuthenticationTicket ticket1 = null;<br/>&nbsp;&nbsp;&nbsp;&nbsp;bool flag1 = false;<br/>&nbsp;&nbsp;&nbsp;&nbsp;try<br/>&nbsp;&nbsp;&nbsp;&nbsp;&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// 从 Cookie 中提取验证票证对象<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ticket1 = FormsAuthenticationModule.ExtractTicketFromCookie(e.Context, this._FormsName, out flag1);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&#125;<br/>&nbsp;&nbsp;&nbsp;&nbsp;catch<br/>&nbsp;&nbsp;&nbsp;&nbsp;&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ticket1 = null;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&#125;<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;if ((ticket1 != null) &amp;&amp; !ticket1.Expired)<br/>&nbsp;&nbsp;&nbsp;&nbsp;&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;FormsAuthenticationTicket ticket2 = ticket1;<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// 刷新票证信息<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (FormsAuthentication.SlidingExpiration)<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ticket2 = FormsAuthentication.RenewTicketIfOld(ticket1);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#125;<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// 创建主体和标识对象<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;e.Context._user = new GenericPrincipal(new FormsIdentity(ticket2), new string[0]);<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (!flag1 &amp;&amp; !ticket2.CookiePath.Equals(&quot;/&quot;))<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cookie1 = e.Context.Request.Cookies[this._FormsName];<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (cookie1 != null)<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cookie1.Path = ticket2.CookiePath;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#125;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#125;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (ticket2 != ticket1)<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if ((flag1 &amp;&amp; (ticket2.CookiePath != &quot;/&quot;)) &amp;&amp; (ticket2.CookiePath.Length &gt; 1))<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ticket2 = new FormsAuthenticationTicket(ticket2.Version, ticket2.Name, ticket2.IssueDate, ticket2.Expiration, ticket2.IsPersistent, ticket2.UserData, &quot;/&quot;);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#125;<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// 加密新的票证，并写入 Cookie。<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;string text1 = FormsAuthentication.Encrypt(ticket2);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (flag1)<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;e.Context.CookielessHelper.SetCookieValue(&#039;F&#039;, text1);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;e.Context.Response.Redirect(e.Context.Request.PathWithQueryString);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#125;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;else<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (cookie1 != null)<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cookie1 = e.Context.Request.Cookies[this._FormsName];<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#125;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (cookie1 == null)<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cookie1 = new HttpCookie(this._FormsName, text1);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cookie1.Path = ticket2.CookiePath;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#125;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (ticket2.IsPersistent)<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cookie1.Expires = ticket2.Expiration;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#125;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cookie1.Value = text1;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cookie1.Secure = FormsAuthentication.RequireSSL;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cookie1.HttpOnly = true;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (FormsAuthentication.CookieDomain != null)<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cookie1.Domain = FormsAuthentication.CookieDomain;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#125;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;e.Context.Response.Cookies.Add(cookie1);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#125;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#125;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&#125;<br/>&nbsp;&nbsp;&#125;<br/>&#125;<br/><br/>private void OnLeave(object source, EventArgs eventArgs)<br/>&#123;<br/>&nbsp;&nbsp;// 调整 URL<br/>&nbsp;&nbsp;if (FormsAuthenticationModule._fAuthChecked &amp;&amp; FormsAuthenticationModule._fAuthRequired)<br/>&nbsp;&nbsp;&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;HttpApplication application1 = (HttpApplication)source;<br/>&nbsp;&nbsp;&nbsp;&nbsp;HttpContext context1 = application1.Context;<br/>&nbsp;&nbsp;&nbsp;&nbsp;if (context1.Response.StatusCode == 0x191)<br/>&nbsp;&nbsp;&nbsp;&nbsp;&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;string text3;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;string text1 = null;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (!string.IsNullOrEmpty(this._LoginUrl))<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;text1 = AuthenticationConfig.GetCompleteLoginUrl(context1, this._LoginUrl);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#125;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if ((text1 == null) &#124;&#124; (text1.Length &lt;= 0))<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;throw new HttpException(SR.GetString(&quot;Auth_Invalid_Login_Url&quot;));<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#125;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;CookielessHelperClass class1 = context1.CookielessHelper;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;string text2 = context1.Request.PathWithQueryString;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (text1.IndexOf(&#039;?&#039;) &gt;= 0)<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;text1 = FormsAuthentication.RemoveQueryStringVariableFromUrl(text1, &quot;ReturnUrl&quot;);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;text3 = text1 + &quot;&amp;ReturnUrl=&quot; + HttpUtility.UrlEncode(text2, context1.Request.ContentEncoding);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#125;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;else<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;text3 = text1 + &quot;?ReturnUrl=&quot; + HttpUtility.UrlEncode(text2, context1.Request.ContentEncoding);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#125;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int num1 = text2.IndexOf(&#039;?&#039;);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if ((num1 &gt;= 0) &amp;&amp; (num1 &lt; (text2.Length - 1)))<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;text2 = FormsAuthentication.RemoveQueryStringVariableFromUrl(text2, &quot;ReturnUrl&quot;);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#125;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;num1 = text2.IndexOf(&#039;?&#039;);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if ((num1 &gt;= 0) &amp;&amp; (num1 &lt; (text2.Length - 1)))<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;text3 = text3 + &quot;&amp;&quot; + text2.Substring(num1 + 1);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#125;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;class1.SetCookieValue(&#039;F&#039;, null);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;class1.RedirectWithDetectionIfRequired(text3, FormsAuthentication.CookieMode);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;context1.Response.Redirect(text3, false);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&#125;<br/>&nbsp;&nbsp;&#125;<br/>&#125;<br/><br/><br/>通过对 FormsAuthenticationModule 代码的分析，我们基本可以确定 ASP.NET Forms 身份验证流程。<br/><br/>1. 拦截系统访问，检查身份验证状态。<br/>2. 如未通过验证，则跳转到指定的 loginUrl 接受用户身份数据验证。<br/>3. 如已通过验证，则从 Cookie 中提取身份验证票证对象。<br/>4. 创建用户标识和主体对象，其中标识对象包含了票证引用。<br/>5. 更新票证过期时间，重新写入 Cookie。<br/>6. 调整URL参数，重定向页面。<br/><br/>在整个验证流程中，我们可以看到几个验证的核心类型。包括：<br/><br/>FormsAuthenticationTicket ：身份验证票证，保存用户名、过期时间、自定义数据等信息。经 FormsAuthentication.Encrypt 方法加密成字符串后保存到 Cookie 或者 URL 参数中。<br/><br/>GenericPrincipal ：用户主体对象。HttpContext.User 就是该类型，用来保存用户身份数据，诸如：FormsIdentity、FormsAuthenticationTicket 等。<br/><br/>FormsIdentity：用户标识对象，可通过 HttpContext.Current.User.Identity 访问。其 Ticket 属性保存了用户身份验证票据引用。<br/><br/>HttpContext：ASP.NET 为每个用户创建的上下文对象，用来保存该用户的相关信息，诸如 Session、GenericPrincipal 等。<br/><br/>FormsAuthentication：ASP.NET Forms 身份验证的核心类之一，其静态属性可访问 Forms Web.config 配置，相关方法用来操作身份验证数据。<br/><br/>[.NET 基于角色安全性验证] 之三：ASP.NET Forms 身份验证<br/><br/>在开发过程中，我们需要做的事情包括：<br/><br/>1. 在 web.config 中设置 Forms 身份验证相关参数。<br/>2. 创建登录页。<br/><br/>登录页中的操作包括：<br/><br/>1. 验证用户名和密码是否正确。<br/>2. 创建身份验证票证对象。<br/>3. 将身份验证票证对象加密成字符串，写入 Cookies。<br/>4. 重定向到原始请求 URL。<br/><br/>1. 简单演示<br/><br/>web.config <br/><br/>&lt;?xml version=&quot;1.0&quot;?&gt;<br/>&lt;configuration&gt;<br/>&nbsp;&nbsp;&lt;system.web&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&lt;compilation debug=&quot;true&quot;/&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&lt;authentication mode=&quot;Forms&quot;&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;forms loginUrl=&quot;~/logon.aspx&quot; &gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;credentials passwordFormat=&quot;Clear&quot;&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;user password=&quot;password&quot;/&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/credentials&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/forms&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&lt;/authentication&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&lt;authorization&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;deny users=&quot;?&quot;/&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&lt;/authorization&gt;<br/>&nbsp;&nbsp;&lt;/system.web&gt;<br/>&lt;/configuration&gt;<br/><br/><br/>logon.aspx <br/><br/>&lt;%@ Page Language=&quot;C#&quot; %&gt;<br/>&lt;!DOCTYPE html PUBLIC &quot;-//W3C//DTD XHTML 1.0 Transitional//EN&quot; &quot;http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd&quot;&gt;<br/><br/>&lt;script runat=&quot;server&quot;&gt;<br/>&nbsp;&nbsp;protected void Button1_Click(object sender, EventArgs e)<br/>&nbsp;&nbsp;&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;if (FormsAuthentication.Authenticate(this.txtUsername.Text, this.txtPassword.Text))<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;FormsAuthentication.RedirectFromLoginPage(this.txtUsername.Text, true);<br/>&nbsp;&nbsp;&nbsp;&nbsp;else<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Response.Write(&quot;用户名或密码错误!&quot;);<br/>&nbsp;&nbsp;&#125;<br/>&lt;/script&gt;<br/><br/>&lt;html xmlns=&quot;http://www.w3.org/1999/xhtml&quot; &gt;<br/>&lt;head runat=&quot;server&quot;&gt;<br/> &lt;title&gt;登录页&lt;/title&gt;<br/>&lt;/head&gt;<br/>&lt;body&gt;<br/> &lt;form runat=&quot;server&quot;&gt;<br/> &lt;div&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;Username: &lt;asp:TextBox runat=&quot;server&quot; Width=&quot;100px&quot; Text=&quot;username&quot;&gt;&lt;/asp:TextBox&gt;&lt;br /&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;Password: &lt;asp:TextBox runat=&quot;server&quot; Width=&quot;100px&quot; Text=&quot;password&quot;&gt;&lt;/asp:TextBox&gt;&lt;br /&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&lt;asp:Button runat=&quot;server&quot; Text=&quot;Sign In&quot; /&gt;<br/> &lt;/div&gt;<br/> &lt;/form&gt;<br/>&lt;/body&gt;<br/>&lt;/html&gt;<br/><br/><br/>2. Forms 验证参数<br/><br/>如果只是某些子目录中的页面访问请求需要进行身份验证，那么可以修改一下根路径下的 web.config。<br/><br/>web.config <br/><br/>&lt;?xml version=&quot;1.0&quot;?&gt;<br/>&lt;configuration&gt;<br/>&nbsp;&nbsp;&lt;system.web&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&lt;compilation debug=&quot;true&quot;/&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&lt;authentication mode=&quot;Forms&quot;&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;forms loginUrl=&quot;~/logon.aspx&quot; &gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;credentials passwordFormat=&quot;Clear&quot;&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;user password=&quot;password&quot;/&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/credentials&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/forms&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&lt;/authentication&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&lt;authorization&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;allow users=&quot;*&quot;/&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&lt;/authorization&gt;<br/>&nbsp;&nbsp;&lt;/system.web&gt;<br/>&lt;/configuration&gt;<br/><br/><br/>然后在需要进行身份验证的子目录中创建一个新的 web.config。<br/><br/>&lt;?xml version=&quot;1.0&quot;?&gt;<br/>&lt;configuration&gt;<br/>&nbsp;&nbsp;&lt;system.web&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&lt;authorization&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;deny users=&quot;?&quot;/&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&lt;/authorization&gt;<br/>&nbsp;&nbsp;&lt;/system.web&gt;<br/>&lt;/configuration&gt;<br/><br/><br/>我们还可以在根路径下的 web.config 中指定相关参数来控制身份验证模式。<br/><br/>cookieless <br/>&nbsp;&nbsp;定义是否使用 Cookie 以及 Cookie 的行为。<br/>&nbsp;&nbsp;.UseCookies <br/>&nbsp;&nbsp;&nbsp;&nbsp;指定无论在什么设备上都始终使用 Cookie。<br/>&nbsp;&nbsp;.UseUri <br/>&nbsp;&nbsp;&nbsp;&nbsp;指定从不使用 Cookie。<br/>&nbsp;&nbsp;.AutoDetect <br/>&nbsp;&nbsp;&nbsp;&nbsp;如果设备配置文件支持 Cookie，则指定使用 Cookie；否则不使用 Cookie。<br/>&nbsp;&nbsp;.UseDeviceProfile <br/>&nbsp;&nbsp;&nbsp;&nbsp;如果浏览器支持 Cookie，则指定使用 Cookie；否则不使用 Cookie。 <br/>&nbsp;&nbsp;&nbsp;&nbsp;对于支持 Cookie 的设备，不尝试通过探测来确定是否已启用 Cookie 支持。<br/> <br/>defaultUrl <br/>&nbsp;&nbsp;定义在身份验证之后用于重定向的默认 URL。 默认值为 &quot;default.aspx&quot;。<br/>&nbsp;&nbsp;当我们直接打开登录页进行登录后，该属性就很重要了。<br/><br/>loginUrl <br/>&nbsp;&nbsp;指定如果找不到任何有效的身份验证 Cookie，将请求重定向到的用于登录的 URL。默认值为 login.aspx。<br/> <br/>name <br/>&nbsp;&nbsp;指定要用于身份验证的 HTTP Cookie。如果正在一台服务器上运行多个应用程序并且每个应用程序都需要<br/>&nbsp;&nbsp;唯一的 Cookie，则必须在每个应用程序的 Web.config 文件中配置 Cookie 名称。默认值为 &quot;.ASPXAUTH&quot;。<br/> <br/>path <br/>&nbsp;&nbsp;为应用程序发出的 Cookie 指定路径。 <br/>&nbsp;&nbsp;默认值是斜杠 (/)，这是因为大多数浏览器是区分大小写的，如果路径大小写不匹配，浏览器不会送回 Cookie。<br/> <br/>timeout <br/>&nbsp;&nbsp;指定 Cookie 过期前逝去的时间（以整数分钟为单位）。持久性 Cookie 不超时。默认值为 &quot;30&quot;（30 分钟）。<br/><br/><br/>更详细信息，请参考 MSDN 文档。<br/>ms-help://MS.MSDNQTR.v80.chs/MS.MSDN.v80/MS.NETDEVFX.v20.chs/dv_ASPNETgenref/html/8163b8b5-ea6c-46c8-b5a9-c4c3de31c0b3.htm<br/><br/>&lt;?xml version=&quot;1.0&quot;?&gt;<br/>&lt;configuration&gt;<br/>&nbsp;&nbsp;&lt;system.web&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&lt;compilation debug=&quot;true&quot;/&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&lt;authentication mode=&quot;Forms&quot;&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;forms loginUrl=&quot;~/logon.aspx&quot; defaultUrl=&quot;index.aspx&quot; timeout=&quot;10&quot;&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;credentials passwordFormat=&quot;Clear&quot;&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;user password=&quot;password&quot;/&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/credentials&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/forms&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&lt;/authentication&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&lt;authorization&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;allow users=&quot;*&quot;/&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&lt;/authorization&gt;<br/>&nbsp;&nbsp;&lt;/system.web&gt;<br/>&lt;/configuration&gt;<br/><br/><br/>3. 验证方法<br/><br/>我们可以使用下面 4 种方法中的一种进行票证写入和重定向操作，其实前 3 种只不过是对第 4 种方法的封装而已。推荐使用 1、4。注意后三种方法不支持cookieless=&quot;UseUri&quot;。<br/><br/>// 1. 使用缺省身份验证票证<br/>FormsAuthentication.RedirectFromLoginPage(&quot;username&quot;, true);<br/><br/>// 2. 使用缺省身份验证票证<br/>FormsAuthentication.SetAuthCookie(&quot;username&quot;, false);<br/>Response.Redirect(FormsAuthentication.GetRedirectUrl(&quot;username&quot;, false));<br/><br/>// 3. 使用缺省身份验证票证<br/>Response.Cookies.Add(FormsAuthentication.GetAuthCookie(&quot;username&quot;, false));<br/>Response.Redirect(FormsAuthentication.GetRedirectUrl(&quot;username&quot;, false));<br/><br/>// 4. 使用自定义身份验证票证<br/>FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(1, &quot;username&quot;, DateTime.Now, DateTime.Now.AddMinutes(10), false, null);<br/>Response.Cookies.Add(new HttpCookie(FormsAuthentication.FormsCookieName, FormsAuthentication.Encrypt(ticket)));<br/>Response.Redirect(FormsAuthentication.GetRedirectUrl(&quot;username&quot;, false));<br/><br/><br/>4. 自定义身份标识类型<br/><br/>MSDN 文档告诉我们，可以在 Global.asax 中通过 Authenticate 事件使用自定义 Principal、Identity 替代 GenericPrincipal、FormsIdentity。因为 Authenticate 事件在 AuthenticateRequest 事件期间引发，因此我们可以在其他模块之前创建用户身份标识对象(FormsAuthenticationEventArgs.User)。<br/><br/>ms-help://MS.MSDNQTR.v80.chs/MS.MSDN.v80/MS.NETDEVFX.v20.chs/cpref12/html/T_System_Web_Security_FormsAuthenticationEventHandler.htm<br/><br/>class // // ...<br/>&#125;<br/>&nbsp;&nbsp;<br/>public void FormsAuthentication_OnAuthenticate(object sender, FormsAuthenticationEventArgs args)<br/>&#123;<br/>&nbsp;&nbsp;if (FormsAuthentication.CookiesSupported)<br/>&nbsp;&nbsp;&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;if (Request.Cookies[FormsAuthentication.FormsCookieName] != null)<br/>&nbsp;&nbsp;&nbsp;&nbsp;&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;try<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;FormsAuthenticationTicket ticket = FormsAuthentication.Decrypt(<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Request.Cookies[FormsAuthentication.FormsCookieName].Value);<br/> <br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;args.User = new MyPrincipal(new MyIdentity (ticket), new string[0]);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#125;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;catch (Exception e)<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// Decrypt method failed.<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#125;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&#125;<br/>&nbsp;&nbsp;&#125;<br/>&nbsp;&nbsp;else<br/>&nbsp;&nbsp;&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;throw new HttpException(&quot;Cookieless Forms Authentication is not &quot; +<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&quot;supported for this application.&quot;);<br/>&nbsp;&nbsp;&#125;<br/><br/>&#125;<br/><br/><br/>当然，还有另外一种简便的方法。<br/><br/>if (!(HttpContext.Current.User is MyPrincipal))<br/>&#123;<br/>&nbsp;&nbsp;HttpContext.Current.User = new MyPrincipal(new MyIdentity(ticket), roles);<br/>&#125;<br/><br/><br/>只不过，你要找一个合适的时机而已。<br/><br/>5. FormsAuthentication<br/><br/>Authenticate <br/>对照存储在应用程序配置文件中的凭据来验证用户名和密码。该方法只能验证存储在 web.config 中的用户名和密码信息，大多数时候我们会用自己的验证方法替代它。<br/><br/>Decrypt <br/>解密从 Cookie 中获取的加密字符串，创建 FormsAuthenticationTicket 对象。 <br/><br/>Encrypt <br/>加密 FormsAuthenticationTicket，返回加密后字符串。<br/><br/>GetRedirectUrl <br/>返回导致重定向到登录页的原始请求 URL。GetRedirectUrl 方法返回查询字符串中使用 ReturnURL 变量名指定的 URL。例如，在 URL http://www.contoso.com/login.aspx?ReturnUrl=caller.aspx 中，GetRedirectUrl 方法返回返回 caller.aspx。如果 ReturnURL 变量不存在，GetRedirectUrl 方法将返回 DefaultUrl 属性中的 URL。<br/><br/>RedirectFromLoginPage <br/>将经过身份验证的用户重定向回最初请求的 URL 或 DefaultUrl 。 <br/><br/>RedirectToLoginPage <br/>将浏览器重定向到登录 URL。 <br/><br/>RenewTicketIfOld <br/>有条件地更新 FormsAuthenticationTicket 的发出日期和时间以及过期日期和时间。 注意该方法只是返回更新后的 FormsAuthenticationTicket 对象，并不会写入 Cookies。<br/><br/>GetAuthCookie <br/>为给定的用户名创建身份验证 Cookie，并不添加到响应的 Cookie 集合或 URL。 <br/><br/>SetAuthCookie <br/>为提供的用户名创建一个身份验证票证，并将其添加到响应的 Cookie 集合或 URL。 <br/><br/>SignOut <br/>从浏览器删除 Forms 身份验证票证。 <br/><br/>6. 票证自定义数据应用<br/><br/>使用自定义票证时，我们可以添加一个 userData 参数。善加利用这个参数还是能带了一些意想不到的好处的，诸如存储用户 VIP 等级编号，所拥有的权限/角色集合等。当然 Cookie 和 URL 参数长度有限，这个自定义数据不能太长。<br/><br/>[.NET 基于角色安全性验证] 之四：ASP.NET 2.0 成员资格和角色管理授权<br/><br/>从严格意义上来说，ASP.NET 2.0 的成员资格、角色管理授权和 .NET 角色安全性没有多大关系。只不过，Microsoft 替我们完成了一些原本需要我们自己进行的工作而已。<br/><br/>在这两种新的技术中使用的&quot;提供程序模型&quot;倒是值得我们好好学习一下，因为这个 IoC 概念非常相似。<br/><br/>成员资格<br/><br/>成员资格提供了通用的用户管理功能，诸如注册、登录、找回密码等，加上与之配套的可视化控件，我们“几乎”不用在编写额外的代码就可以工作。实际上真是如此吗？MemebershipUser 属性太少，显然不大适合我们的商业应用；注册和登录控件有缺乏验证码，缺乏安全性；…… 当然我们还可以使用自定义验证和提供程序，不过如此一来还不如自己参考&quot;提供者模型&quot;开发一套呢，毕竟独立的 Passport 才是我们所需要的，更不要说和既有系统进行整合了，总之 ASP.NET 提供的这个新功能，食之无味，弃之可惜。有关成员资格和角色管理的使用不是本文的重点，详情可参考 MSDN 文档。<br/><br/>成员资格由 Membership、MembershipUser、MembershipProvider 组成，Membership 是个静态类，为用户提供了大量用户相关的操作方法，MembershipUser 则是用户实体类，除了用户属性外也有一些用户自身的操作方法，而 MembershipProvider 自然就是提供程序的基础抽象类了，它规范了提供程序的接口。<br/><br/><br/><br/><br/>我们分析一下 Membership 代码(局部代码)，看看如何获取真实的目标提供程序对象的。其实过程也很简单，读取配置获取类型信息，然后用反射创建目标提供程序对象实例。<br/><br/>System.System.Web.Security.Membership <br/><br/>// 我们从 ValidateUser 方法入手。<br/>public static bool ValidateUser(string username, string password)<br/>&#123;<br/>&nbsp;&nbsp;// 通过 Provider 属性获取提供程序对象<br/>&nbsp;&nbsp;return Membership.Provider.ValidateUser(username, password);<br/>&#125;<br/><br/>public static MembershipProvider Provider<br/>&#123;<br/>&nbsp;&nbsp;get<br/>&nbsp;&nbsp;&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;// 应该是通过 Initialize() 方法获取，并赋值给 s_Provider 变量的。<br/>&nbsp;&nbsp;&nbsp;&nbsp;Membership.Initialize();<br/>&nbsp;&nbsp;&nbsp;&nbsp;return Membership.s_Provider;<br/>&nbsp;&nbsp;&#125;<br/>&#125;<br/><br/>private static void Initialize()<br/>&#123;<br/>&nbsp;&nbsp;// 从配置文件中读取配置信息<br/>&nbsp;&nbsp;RuntimeConfig config1 = RuntimeConfig.GetAppConfig();<br/>&nbsp;&nbsp;MembershipSection section1 = config1.Membership;<br/><br/>&nbsp;&nbsp;Membership.s_Providers = new MembershipProviderCollection();<br/><br/>&nbsp;&nbsp;// 使用 ProvidersHelper.InstantiateProviders 方法从配置信息中读取设置。<br/>&nbsp;&nbsp;ProvidersHelper.InstantiateProviders(section1.Providers, Membership.s_Providers, typeof(MembershipProvider));<br/><br/>&nbsp;&nbsp;// 将缺省提供程序对象赋值给 s_Provider 变量。<br/>&nbsp;&nbsp;Membership.s_Provider = Membership.s_Providers[section1.DefaultProvider];<br/>&nbsp;&nbsp;Membership.s_Initialized = true;<br/>&#125;<br/><br/>public static void ProvidersHelper.InstantiateProviders(ProviderSettingsCollection configProviders, ProviderCollection providers, Type providerType)<br/>&#123;<br/>&nbsp;&nbsp;// 循环读取配置信息，并创建提供程序对象。<br/>&nbsp;&nbsp;foreach (ProviderSettings settings1 in configProviders)<br/>&nbsp;&nbsp;&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;providers.Add(ProvidersHelper.InstantiateProvider(settings1, providerType));<br/>&nbsp;&nbsp;&#125;<br/>&#125;<br/><br/>public static ProviderBase ProvidersHelper.InstantiateProvider(ProviderSettings providerSettings, Type providerType)<br/>&#123;<br/>&nbsp;&nbsp;ProviderBase base1 = null;<br/>&nbsp;&nbsp;try<br/>&nbsp;&nbsp;&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;// 调用 HttpRuntime.CreatePublicInstance 创建提供程序对象。<br/>&nbsp;&nbsp;&nbsp;&nbsp;base1 = (ProviderBase)HttpRuntime.CreatePublicInstance(type1);<br/>&nbsp;&nbsp;&#125;<br/>&nbsp;&nbsp;catch (Exception exception1)<br/>&nbsp;&nbsp;&#123;<br/>&nbsp;&nbsp;&#125;<br/>&nbsp;&nbsp;return base1;<br/>&#125;<br/><br/>internal static object HttpRuntime.CreatePublicInstance(Type type)<br/>&#123;<br/>&nbsp;&nbsp;// 利用反射创建对象<br/>&nbsp;&nbsp;return Activator.CreateInstance(type);<br/>&#125;<br/><br/><br/>下面我们分析一下 Login 控件的源码，我们会发现其内部不过是自动调用 Membership 和 MembershipProvider 进行操作而已。<br/><br/>System.Web.UI.WebControls.Login <br/><br/>private void AttemptLogin()<br/>&#123;<br/>&nbsp;&nbsp;if ((this.Page == null) &#124;&#124; this.Page.IsValid)<br/>&nbsp;&nbsp;&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;LoginCancelEventArgs args1 = new LoginCancelEventArgs();<br/>&nbsp;&nbsp;&nbsp;&nbsp;this.OnLoggingIn(args1);<br/>&nbsp;&nbsp;&nbsp;&nbsp;if (!args1.Cancel)<br/>&nbsp;&nbsp;&nbsp;&nbsp;&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;AuthenticateEventArgs args2 = new AuthenticateEventArgs();<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// 调用核心代码<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;this.OnAuthenticate(args2);<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// 看到了什么？标准的身份验证代码。详情可以查看上一篇Blog。<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (args2.Authenticated)<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;FormsAuthentication.SetAuthCookie(this.UserNameInternal, this.RememberMeSet);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;this.OnLoggedIn(EventArgs.Empty);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;this.Page.Response.Redirect(this.GetRedirectUrl(), false);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#125;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;else<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;this.OnLoginError(EventArgs.Empty);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (this.FailureAction == LoginFailureAction.RedirectToLoginPage)<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;FormsAuthentication.RedirectToLoginPage(&quot;loginfailure=1&quot;);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#125;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ITextControl control1 = (ITextControl)this.TemplateContainer.FailureTextLabel;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (control1 != null)<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;control1.Text = this.FailureText;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#125;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#125;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&#125;<br/>&nbsp;&nbsp;&#125;<br/>&#125;<br/><br/>protected virtual void OnAuthenticate(AuthenticateEventArgs e)<br/>&#123;<br/>&nbsp;&nbsp;AuthenticateEventHandler handler1 = (AuthenticateEventHandler)base.Events[Login.EventAuthenticate];<br/>&nbsp;&nbsp;if (handler1 != null)<br/>&nbsp;&nbsp;&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;// 用户使用了自定义身份验证服务<br/>&nbsp;&nbsp;&nbsp;&nbsp;handler1(this, e);<br/>&nbsp;&nbsp;&#125;<br/>&nbsp;&nbsp;else<br/>&nbsp;&nbsp;&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;// 使用成员资格提供程序<br/>&nbsp;&nbsp;&nbsp;&nbsp;this.AuthenticateUsingMembershipProvider(e);<br/>&nbsp;&nbsp;&#125;<br/>&#125;<br/><br/>private void AuthenticateUsingMembershipProvider(AuthenticateEventArgs e)<br/>&#123;<br/>&nbsp;&nbsp;// 使用 LoginUtil.GetProvider 获取成员资格提供程序对象，并调用其 ValidateUser 进行身份验证。<br/>&nbsp;&nbsp;e.Authenticated = LoginUtil.GetProvider(this.MembershipProvider).ValidateUser(this.UserNameInternal, this.PasswordInternal);<br/>&#125;<br/><br/>internal static LoginUtil.MembershipProvider GetProvider(string providerName)<br/>&#123;<br/>&nbsp;&nbsp;if (string.IsNullOrEmpty(providerName))<br/>&nbsp;&nbsp;&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;return Membership.Provider;<br/>&nbsp;&nbsp;&#125;<br/><br/>&nbsp;&nbsp;// 调用 Membership 相关属性，获取提供程序对象。<br/>&nbsp;&nbsp;MembershipProvider provider1 = Membership.Providers[providerName];<br/>&nbsp;&nbsp;if (provider1 == null)<br/>&nbsp;&nbsp;&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;throw new HttpException(SR.GetString(&quot;WebControl_CantFindProvider&quot;));<br/>&nbsp;&nbsp;&#125;<br/>&nbsp;&nbsp;return provider1;<br/>&#125;<br/><br/><br/>角色管理授权<br/><br/>在实际开发中，除了用户(User)和角色(Role)以外，我们还需要权限(Permission)这个概念。角色更像是用户组(Users Group)，用户可以加入一个或多个用户组，每个用户组又拥有多个权限。有了权限，我们就可以动态赋予用户组不同的权力，比如我们可以临时授予 A 组执行 XX 的权力，三天后我们再取消该权力，显然没有权限的设计会缺乏灵活性。<br/><br/>ASP.NET 2.0 的角色管理授权，由 RoleManagerModule、Roles、RoleProvider、RolePrincipa 共同组成。系统会自动载入 RoleManagerModule 这个 HttpModule，Roles 为用户提供角色相关的操作方法，而 RoleProvider 自然是提供程序的抽象类了。至于 RolePrincipa 干什么用，看看下面的代码自然就明白了。<br/><br/>System.Web.Security.RoleManagerModule <br/><br/>private void OnEnter(object source, EventArgs eventArgs)<br/>&#123;<br/>&nbsp;&nbsp;// ...<br/>&nbsp;&nbsp;// 注意下面的代码， RoleManagerModule 使用 RolePrincipal 替代了缺省的 GenericPrincipal。<br/>&nbsp;&nbsp;if (!(context1.User is RolePrincipal))<br/>&nbsp;&nbsp;&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;context1.User = new RolePrincipal(context1.User.Identity);<br/>&nbsp;&nbsp;&#125;<br/>&#125;<br/><br/>[.NET 基于角色安全性验证] 之五：跨应用程序进行 Forms 身份验证<br/><br/>MSDN 文档<br/><br/>ms-help://MS.MSDNQTR.v80.chs/MS.MSDN.v80/MS.VisualStudio.v80.chs/dv_aspnetcon/html/99e2f9e8-5b97-4a4d-a4ed-5f93276053b7.htm<br/><br/>ASP.NET 支持在分布式环境中（跨单个服务器上的多个应用程序或在网络场中）进行 Forms 身份验证。如果启用了跨多个 ASP.NET 应用程序的 Forms 身份验证，则当用户在应用程序之间切换时，不需要对他们重新进行身份验证。<br/><br/>要配置跨应用程序的 Forms 身份验证，请在 forms 和 machineKey 配置节中设置若干属性，以便值对于参与共享 Forms 身份验证的所有应用程序都是相同的。 <br/><br/>下面的示例演示了 Web.config 文件的 Authentication 节。除非另行说明，否则 name、protection、path、validationKey 和 decryptionKey 属性必须在所有应用程序中都完全相同。同样，用于 Cookie 数据的加密和验证密钥以及加密方案也必须完全相同。如果设置不匹配，则不能共享 Cookie。<br/><br/>web.config <br/><br/>&lt;configuration&gt;<br/>&nbsp;&nbsp;&lt;system.web&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&lt;authentication mode=&quot;Forms&quot; &gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;!-- The name, protection, and path attributes must match <br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;exactly in each Web.config file. --&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;forms loginUrl=&quot;login.aspx&quot;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;name=&quot;.ASPXFORMSAUTH&quot; <br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;protection=&quot;All&quot; <br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;path=&quot;/&quot; <br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;timeout=&quot;30&quot; /&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&lt;/authentication&gt;<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;&lt;!-- Validation and decryption keys must exactly match and cannot<br/>&nbsp;&nbsp;&nbsp;&nbsp;be set to &quot;AutoGenerate&quot;. The validation algorithm must also <br/>&nbsp;&nbsp;&nbsp;&nbsp;be the same. --&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&lt;machineKey<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;validationKey=&quot;C50B3C89CB21F4...BE&quot; <br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;decryptionKey=&quot;8A9BE8FD67AF6979E7D20198CFEA50DD3D3799C77AF2B72F&quot; <br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;validation=&quot;SHA1&quot; /&gt;<br/>&nbsp;&nbsp;&lt;/system.web&gt;<br/>&lt;/configuration&gt;<br/><br/><br/>发出 Cookie 之后，将根据 Cookie 自身中的 Expires 值跟踪 Cookie 的到期时间。这意味着如果两个应用程序具有不同的 Timeout 属性，则将在 Cookie 的整个生存期中保留最初发出每个 Cookie 时设置的到期日期和时间。当更新 Cookie 时，Cookie 的原始到期时间用于计算新到期时间。使用配置 Timeout 值的唯一时间就是最初创建 Cookie 的时间。<br/><br/>-----------------------------------------------------<br/><br/>雨痕补充<br/><br/>按照上述操作，我们就可以在单个或多个服务器的网络场中实现 &quot;跨应用程序进行 Forms 身份验证&quot;，这个有点 &quot;单点登录(SSO, Single Sign On)&quot; 的意思，不过有几点还是要注意一下。<br/><br/>1. 如果使用新打开的浏览器窗体打开另外的应用程序网站，则必须创建持久 Cookie (跨浏览器会话保存 Cookie) 时，才能在多个应用程序中共享登录信息。<br/>(&quot;新打开的浏览器窗体&quot; 这话有点拗口，也就说这个窗体不是原浏览器弹出的，而是我们使用快捷方式新打开的进程。)<br/><br/>FormsAuthentication.RedirectFromLoginPage(&quot;username&quot;, true);<br/><br/><br/>2. MachineKey 创建方法。<br/><br/>using System.Text;<br/>using System.Security.Cryptography;<br/><br/>public class MachineKey<br/>&#123;<br/>&nbsp;&nbsp;const int validationKeyLength = 64;<br/>&nbsp;&nbsp;const int decryptionKeyLength = 24;<br/>&nbsp;&nbsp;private RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();<br/><br/>&nbsp;&nbsp;public string GenerateKey()<br/>&nbsp;&nbsp;&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;return string.Format(&quot;&lt;machineKey validationKey=&#92;&quot;&#123;0&#125;&#92;&quot;&#92;r&#92;ndecryptionKey=&#92;&quot;&#123;1&#125;&#92;&quot;&#92;r&#92;nvalidation=&#92;&quot;SHA1&#92;&quot;/&gt;&quot;,<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;BytesToHex(GenerateKeyBytes(validationKeyLength)), BytesToHex(GenerateKeyBytes(decryptionKeyLength)));<br/>&nbsp;&nbsp;&#125;<br/><br/>&nbsp;&nbsp;private byte[] GenerateKeyBytes(int cb)<br/>&nbsp;&nbsp;&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;byte[] rndData = new byte[cb];<br/>&nbsp;&nbsp;&nbsp;&nbsp;rng.GetBytes(rndData);<br/>&nbsp;&nbsp;&nbsp;&nbsp;return rndData;<br/>&nbsp;&nbsp;&#125;<br/><br/>&nbsp;&nbsp;private string BytesToHex(byte[] key)<br/>&nbsp;&nbsp;&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;StringBuilder sb = new StringBuilder();<br/>&nbsp;&nbsp;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;for (int i = 0; i &lt; key.Length; ++i)<br/>&nbsp;&nbsp;&nbsp;&nbsp;&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sb.Append(String.Format(&quot;&#123;0:X2&#125;&quot;, key[i]));<br/>&nbsp;&nbsp;&nbsp;&nbsp;&#125;<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;return sb.ToString();<br/>&nbsp;&nbsp;&#125;<br/>&#125;<br/><br/><br/>使用方法<br/><br/>protected void Page_Load(object sender, EventArgs e)<br/>&#123;<br/>&nbsp;&nbsp;Response.Write(HttpUtility.HtmlEncode(new MachineKey().GenerateKey()));<br/>&#125;<br/><br/> <br/>
]]>
</description>
</item><item>
<link>http://www.ywqi.com/read.php/222.htm</link>
<title><![CDATA[一个关于格式化的代码]]></title> 
<author>bgaidu &lt;admin@yourname.com&gt;</author>
<category><![CDATA[asp.net]]></category>
<pubDate>Sun, 04 Jan 2009 08:33:57 +0000</pubDate> 
<guid>http://www.ywqi.com/read.php/222.htm</guid> 
<description>
<![CDATA[ 
	&lt;asp:Label&nbsp;&nbsp; runat=&quot;server&quot;&nbsp;&nbsp; Text=&#039;&lt;%#&nbsp;&nbsp; DataBinder.Eval(Container,&nbsp;&nbsp; &quot;DataItem.Sex&quot;).ToString()=&quot;1&quot;?&quot;男&quot;:&quot;女&quot;&nbsp;&nbsp; %&gt;&#039;&nbsp;&nbsp; ID=&quot;Label1&quot;&gt;&nbsp;&nbsp;<br/><br/>、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、<br/> protected void GridView1_RowDataBound(object sender, GridViewRowEventArgs e)<br/>&nbsp;&nbsp;&nbsp;&nbsp;&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (e.Row.RowIndex &gt;= 0)<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#123;<br/>if(e.Row.Cells[2].Text.ToString()==&quot;1&quot;)<br/>&#123;e.Row.Cells[2].Text.ToString()==&quot;AAA&quot;&#125;<br/>....<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#125;<br/>
]]>
</description>
</item><item>
<link>http://www.ywqi.com/read.php/221.htm</link>
<title><![CDATA[C# 关于密码加密 (转载) ]]></title> 
<author>bgaidu &lt;admin@yourname.com&gt;</author>
<category><![CDATA[asp.net]]></category>
<pubDate>Mon, 29 Dec 2008 01:32:59 +0000</pubDate> 
<guid>http://www.ywqi.com/read.php/221.htm</guid> 
<description>
<![CDATA[ 
	<br/>//SHA-1算法<br/>string password = System.Web.Security.FormsAuthentication.HashPasswordForStoringInConfigFile(Password.Text, &quot;SHA1&quot;);<br/>//MD5算法<br/>string password1 = System.Web.Security.FormsAuthentication.HashPasswordForStoringInConfigFile(Password.Text, &quot;MD5&quot;);<br/><br/>加密后生成不可逆密文保存到数据库中。用户登录时用加密计算后的密文与数据库中的密码密文比较。一致则通过验证，不一致则返回登录错误。<br/>这种加密算法是不可逆的，所以除了用户自己，其他人无法得知用户的真实密码内容。<br/><br/>SHA-1算法和MD5算法的区别：<br/>SHA-1比MD5多32位密文，所以更安全。由于同样的原因，MD5比SHA-1的运算速度更快。 
]]>
</description>
</item><item>
<link>http://www.ywqi.com/read.php/220.htm</link>
<title><![CDATA[C#中StringBuilder类的使用]]></title> 
<author>bgaidu &lt;admin@yourname.com&gt;</author>
<category><![CDATA[winform]]></category>
<pubDate>Mon, 29 Dec 2008 00:43:25 +0000</pubDate> 
<guid>http://www.ywqi.com/read.php/220.htm</guid> 
<description>
<![CDATA[ 
	String 对象是不可改变的。每次使用 System.String 类中的方法之一时，都要在内存中创建一个新的字符串对象，这就需要为该新对象分配新的空间。在需要对字符串执行重复修改的情况下，与创建新的 String 对象相关的系统开销可能会非常昂贵。如果要修改字符串而不创建新的对象，则可以使用 System.Text.StringBuilder 类。例如，当在一个循环中将许多字符串连接在一起时，使用 StringBuilder 类可以提升性能。<br/><br/>　　通过用一个重载的构造函数方法初始化变量，可以创建 StringBuilder 类的新实例，正如以下示例中所阐释的那样。&lt;br/&gt;<br/>StringBuilder MyStringBuilder = new StringBuilder(&quot;Hello World!&quot;);<br/><br/>(一)设置容量和长度<br/>　　虽然 StringBuilder 对象是动态对象，允许扩充它所封装的字符串中字符的数量，但是您可以为它可容纳的最大字符数指定一个值。此值称为该对象的容量，不应将它与当前 StringBuilder 对象容纳的字符串长度混淆在一起。例如，可以创建 StringBuilder 类的带有字符串“Hello”（长度为 5）的一个新实例，同时可以指定该对象的最大容量为 25。当修改 StringBuilder 时，在达到容量之前，它不会为其自己重新分配空间。当达到容量时，将自动分配新的空间且容量翻倍。可以使用重载的构造函数之一来指定 StringBuilder 类的容量。以下代码示例指定可以将 MyStringBuilder 对象扩充到最大 25 个空白。<br/>StringBuilder MyStringBuilder = new StringBuilder(&quot;Hello World!&quot;, 25);<br/>另外，可以使用读/写 Capacity 属性来设置对象的最大长度。以下代码示例使用 Capacity 属性来定义对象的最大长度。<br/>MyStringBuilder.Capacity = 25;<br/><br/>(二)下面列出了此类的几个常用方法：<br/>(1)Append 方法可用来将文本或对象的字符串表示形式添加到由当前 StringBuilder 对象表示的字符串的结尾处。以下示例将一个 StringBuilder 对象初始化为“Hello World”，然后将一些文本追加到该对象的结尾处。将根据需要自动分配空间。<br/>StringBuilder MyStringBuilder = new StringBuilder(&quot;Hello World!&quot;);<br/>MyStringBuilder.Append(&quot; What a beautiful day.&quot;);<br/>Console.WriteLine(MyStringBuilder);<br/>此示例将 Hello World! What a beautiful day. 显示到控制台。<br/><br/>(2)AppendFormat 方法将文本添加到 StringBuilder 的结尾处，而且实现了 IFormattable 接口，因此可接受格式化部分中描述的标准格式字符串。可以使用此方法来自定义变量的格式并将这些值追加到 StringBuilder 的后面。以下示例使用 AppendFormat 方法将一个设置为货币值格式的整数值放置到 StringBuilder 的结尾。<br/>int MyInt = 25; <br/>StringBuilder MyStringBuilder = new StringBuilder(&quot;Your total is &quot;);<br/>MyStringBuilder.AppendFormat(&quot;&#123;0:C&#125; &quot;, MyInt);<br/>Console.WriteLine(MyStringBuilder);<br/>此示例将 Your total is $25.00 显示到控制台。<br/><br/>(3)Insert 方法将字符串或对象添加到当前 StringBuilder 中的指定位置。以下示例使用此方法将一个单词插入到 StringBuilder 的第六个位置。<br/>StringBuilder MyStringBuilder = new StringBuilder(&quot;Hello World!&quot;);<br/>MyStringBuilder.Insert(6,&quot;Beautiful &quot;);<br/>Console.WriteLine(MyStringBuilder);<br/>此示例将 Hello Beautiful World! 显示到控制台。<br/><br/>(4)可以使用 Remove 方法从当前 StringBuilder 中移除指定数量的字符，移除过程从指定的从零开始的索引处开始。以下示例使用 Remove 方法缩短 StringBuilder。<br/>StringBuilder MyStringBuilder = new StringBuilder(&quot;Hello World!&quot;);<br/>MyStringBuilder.Remove(5,7);<br/>Console.WriteLine(MyStringBuilder);<br/>此示例将 Hello 显示到控制台。<br/><br/>(5)使用 Replace 方法，可以用另一个指定的字符来替换 StringBuilder 对象内的字符。以下示例使用 Replace 方法来搜索 StringBuilder 对象，查找所有的感叹号字符 (!)，并用问号字符 (?) 来替换它们。<br/>StringBuilder MyStringBuilder = new StringBuilder(&quot;Hello World!&quot;);<br/>MyStringBuilder.Replace(&#039;!&#039;, &#039;?&#039;);<br/>Console.WriteLine(MyStringBuilder);
]]>
</description>
</item><item>
<link>http://www.ywqi.com/read.php/219.htm</link>
<title><![CDATA[ASP.NET获取IP与MAC地址的方法]]></title> 
<author>bgaidu &lt;admin@yourname.com&gt;</author>
<category><![CDATA[asp.net]]></category>
<pubDate>Fri, 26 Dec 2008 02:02:21 +0000</pubDate> 
<guid>http://www.ywqi.com/read.php/219.htm</guid> 
<description>
<![CDATA[ 
	　获取服务器的IP地址方法以DNS法较为简单实用，如下：<br/>private void ButtonIP_Click(object sender, System.EventArgs e)<br/>&#123; <br/>Syste&nbsp;&nbsp;m.Net.IPAddress[] addressList = Dns.GetHostByName(Dns.GetHostName()).AddressList;<br/>if ( addressList.Length>1)&#123; TextLIP.Text = addressList[0].ToString();<br/>TextSIP.Text = addressList[1].ToString();<br/>&#125;<br/>else<br/>&#123;<br/>TextLIP.Text = addressList[0].ToString();<br/>TextSIP.Text = "没有可用的连接";<br/>&#125;<br/>&#125;<br/> <br/><br/>　　获取服务器的IP地址与MAC地址另一方法如下：<br/><br/>using System.Management;string stringMAC = "";<br/>string stringIP = "";<br/>ManagementClass MC = new ManagementClass "Win32_NetworkAdapterConfiguration");<br/>ManagementObjectCollection MOC= MC.GetInstances();<br/>foreach(ManagementObject MO in MOC)<br/>&#123;<br/>if ((bool)MO["IPEnabled"] == true)<br/>&#123;<br/>stringMAC += MO["MACAddress"].ToString();<br/>TextMAC.Text = stringMAC.ToString();string[] IPAddresses = (string[]) MO["IPAddress"];<br/>if(IPAddresses.Length > 0)<br/>stringIP = IPAddresses[0];<br/>TextIP.Text = stringIP.ToString();<br/>&#125;<br/>&#125;<br/> <br/><br/><br/><br/>　　获取客户端本机的IP地址相当简易，方法如下：<br/><br/>using System.Net;TextIP.Text=Page.Request.UserHostAddress;<br/> <br/><br/><br/><br/>　　如要获取客户端本机的MAC地址就相对复杂些，得导入调用两个API，用ARP协议获取，但这样只能获取到同网段机器的MAC，对于跨网段的得利用IP扫描或cmd中nbtstat命令获取MAC地址。也可通过读取系统注册表值或WMI的数据库来获取。各位如有简易可行的好办法请留言告诉我。 <br/><br/>
]]>
</description>
</item><item>
<link>http://www.ywqi.com/read.php/215.htm</link>
<title><![CDATA[XML开发优秀工具—XmlPad ]]></title> 
<author>bgaidu &lt;admin@yourname.com&gt;</author>
<category><![CDATA[资料]]></category>
<pubDate>Wed, 24 Dec 2008 08:39:38 +0000</pubDate> 
<guid>http://www.ywqi.com/read.php/215.htm</guid> 
<description>
<![CDATA[ 
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;最近做了个项目，主要技术为XML，为此有必要挑选一款合适的XML开发工具。下面是各主要工具特性比较：<br/><br/>1.XmlSpy<br/>名气很大的一款工具。<br/>优点：功能强，可以说是一个面面俱到的XML IDE （我想大部分人可能只用到了其中小部分功能），提供了对包括Web Service在内的等各种XML技术的强大支持。<br/>缺点：体积大，启动慢，上手较难，操作复杂，还有一个是界面不够简洁、大方（个人观点）。<br/><br/>2.XmlWriter<br/>这也是一款老牌工具了。<br/>优点：界面简洁、漂亮，提供了对常用功能的支持，包括代码智能提示、Well-Formed检测、Validated检测(DTD,Schema)等，上手块，操作简便。<br/>缺点：不支持中文。<br/><br/>3.XmlPad<br/>这是在Google中搜到的一款优秀的XML开发工具，国内网站介绍不多，具有如下特色：<br/>a.界面简洁、大方（默认为Visual Studio.net风格，可切换）<br/>b.包括了各种常用功能，代码智能提示、Validated检测(DTD,Schema)等，其中最有特色也是本人最喜爱的就属其提供的三种XML数据浏览视图了，包括Source、Grid View、Table View，可以方便的进行各种方式的数据浏览。<br/>c.由于没有多余功能，所以操作非常方便，舒适。<br/>d.还有重要的一点，他是免费软件。<br/><br/>XMLPad毫无悬念的成为了我的首选XML开发工具，我甚至找不出它的缺点（可能跟我用的时间不长也有关系）。可以去它的官网免费下载，最新版本为3.0.1.3，地址：www.wmhelp.com。 <br/>
]]>
</description>
</item><item>
<link>http://www.ywqi.com/read.php/214.htm</link>
<title><![CDATA[从SqlServer中随机读取记录 ]]></title> 
<author>bgaidu &lt;admin@yourname.com&gt;</author>
<category><![CDATA[winform]]></category>
<pubDate>Wed, 24 Dec 2008 08:38:51 +0000</pubDate> 
<guid>http://www.ywqi.com/read.php/214.htm</guid> 
<description>
<![CDATA[ 
	&nbsp;&nbsp;&nbsp;&nbsp;SqlServer本身并没有提供随机读取记录的功能，但我们可以通过一些方法来实现这个目的。本文介绍了其中几种方法并比较了各自的优劣。<br/><br/>方法一：<br/>&nbsp;&nbsp;&nbsp;&nbsp;直接通过Sql语句实现，如：<br/><br/>select top n *<br/>from tableA<br/>order by newid()<br/>&nbsp;&nbsp;&nbsp;&nbsp;这是最简单的方法，通过调用SqlServer的newid()函数(产生GUID—全局唯一标志符)来产生随机记录。<br/>&nbsp;&nbsp;&nbsp;&nbsp;采用这种方法时，需要将表中所有记录与newid()生成的值进行比较从而进行排序。因此，如果表中的记录较多，操作会非常缓慢。<br/><br/>方法二：<br/>&nbsp;&nbsp;&nbsp;&nbsp;假设表中有一个自增长主键，增量为1。这时我们可以这样处理，取出主键的边界值（最大值和最小值），然后通过一个算法得到介于（包括）两个边界之间的随机值，最后按照这个值取出对应记录。下面是C#的例子：<br/><br/>//生成随机数<br/>Random rand = new Random();<br/>int num = rand.Next(MinVal,MaxVal + 1); //MinVal为主键的最小值，MaxVal为主键的最大值<br/><br/>//读取记录的Sql字符串<br/>string SqlStr = "select * from tableA where PK = " + num;<br/>&nbsp;&nbsp;&nbsp;&nbsp;这种方法较前一种方法的操作速度有了较大提高（特别是在大数据量的情况下）。但只有当主键值是连续的，中间没有断开的情况，并且增量为1时才能用这种方法。那么，如何才能解决这个问题呢？请继续往下看。<br/><br/>方法三：<br/>&nbsp;&nbsp;&nbsp;&nbsp;对方法二进行了改进。主要思路是，将表中所有的主键值读进一个数组，从数组中随机读出一个值，按照这个值取出对应记录。下面是C#的例子：<br/><br/>//将主键值读进ArrayList<br/>ArrayList DataIndex = new ArrayList();<br/>while (sdr.Read()) //sdr为存放所有主键值的SqlDataReader<br/>&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;DataIndex.Add(sdr[0]); //存入ArrayList<br/>&#125;<br/><br/>//从ArrayList中随机读取数据项<br/>Random rand = new Random();<br/>int num = Convert.ToInt32(DataIndex[rand.Next(DataIndex.Length)]);<br/><br/>//读取记录的Sql字符串<br/>string SqlStr = "select * from tableA where PK = " + num;<br/>&nbsp;&nbsp;&nbsp;&nbsp;这样不管主键是否为自增长字段，也不管数值是否连续，都能够应付自如了。经过笔者的测试，在数据量为50万的情况下代码运行速度几乎没有受到什么影响，可见ArrayList的操作性能是可以信赖的。<br/><br/>
]]>
</description>
</item><item>
<link>http://www.ywqi.com/read.php/213.htm</link>
<title><![CDATA[ASP.NET实用技巧(一) ]]></title> 
<author>bgaidu &lt;admin@yourname.com&gt;</author>
<category><![CDATA[asp.net]]></category>
<pubDate>Wed, 24 Dec 2008 08:38:14 +0000</pubDate> 
<guid>http://www.ywqi.com/read.php/213.htm</guid> 
<description>
<![CDATA[ 
	&nbsp;&nbsp;&nbsp;&nbsp; 本文主要介绍ASP.NET编程中的一些常用方法，内容涉及代码技巧、性能优化等方面。<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1.跟踪页面执行<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;设置断点是页面调试过程中的常用手段，除此之外，还可以通过查看页面的跟踪信息进行错误排查以及性能优化。ASP.NET中启用页面跟踪非常方便，只需在Page指令中加入Trace=&quot;True&quot;属性即可：<br/>&lt;%@ Page Language=&quot;C#&quot; Trace=&quot;true&quot;&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;跟踪信息可以分为两类：<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;a.页面执行详细情况<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;其中主要包括页面生命周期中各事件列表、控件树列表(可以查看每个控件的HTML字节数以及ViewState字节数)、Session状态、Application状态、Cookie集合、QueryString集合、服务器变量等信息。<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;b.自定义跟踪信息<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;通过在页面代码中调用Trace.Write()或Trace.Warn()方法便可将指定内容写入跟踪信息中的&quot;Trace Information&quot;节。就算页面发生了错误，跟踪信息还是会显示出来，并且在发布应用程序时无需删除相关的跟踪代码，只需从Page指令中移除Trace属性即可。 <br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2.在服务器端控件中添加客户端属性<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;我们有时会向服务器端控件中添加一些特殊的属性，这类属性不需要服务器端的处理，只需简单的发送至客户端即可，我们不妨称其为客户端属性，例如HTML属性或自定义属性(可能用于实现某种特定的JavaScript功能)。可以通过以下几种方法达到这个目的：<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;a.直接向控件添加客户端属性<br/><br/><br/>&lt;asp:Button ID=&quot;MyButton&quot; Text=&quot;ClickMe&quot; onmouseover=&quot;this.style.cursor=&#039;pointer&#039;&quot; runat=&quot;server&quot; /&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;其中的onmouseover是客户端属性，注意，编译器是允许这种写法的，但会显示警告。<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;b.调用内置方法<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;可以通过调用WebControl.Attributes.Add()方法为控件添加客户端属性，如下所示：<br/><br/>MyButton.Attributes.Add(&quot;onmouseover&quot;, &quot;this.style.cursor=&#039;pointer&#039;&quot;);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;这也是最常用的方法。<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;c.创建自定义控件<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;如果某个类型的服务器端控件中经常需要添加客户端属性，则可以考虑创建一个继承自该服务器端控件的自定义控件，其中包含特定的客户端属性。<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;正是考虑到这一点，ASP.NET 2.0中为按钮控件(包括Button、LinkButton、ImageButton控件)提供了OnClientClick属性，可以这样写：<br/><br/>MyButton.OnClientClick = &quot;alert(&#039;Hello!&#039;)&quot;;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;真是贴心的功能！<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;3.表单数据的服务器端验证<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;将数据验证任务从服务器端迁移到客户端的过程促使了JavaScript的产生，这也是我们沿用至今的一种方式。但只有在保证客户端JavaScript正常运行的前提下，这种方式才能发挥其作用。不幸的是，总有一些例外，比如浏览器不支持JavaScript，或者用户刻意关闭了浏览器的JavaScript功能，这就导致了第一重防护失效。比较保险的做法是加入第二重防护，即对用户提交的数据进行服务器端验证，但这无疑将增加开发者的工作量。<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ASP.NET 2.0提供了一系列表单数据验证控件，可以非常轻松的完成客户端及服务器端的双重数据验证任务。但要使服务器端验证功能发挥作用，还需要用到Page.IsValid属性，请看下面的例子：<br/><br/><br/>&lt;form id=&quot;MyForm&quot; runat=&quot;server&quot;&gt;<br/>&nbsp;&nbsp;&lt;div&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;姓名：&lt;asp:TextBox ID=&quot;txtName&quot; runat=&quot;server&quot;&gt;&lt;/asp:TextBox&gt; &lt;asp:RequiredFieldValidator ID=&quot;RequiredFieldValidator1&quot; ControlToValidate=&quot;txtName&quot; ErrorMessage=&quot;请填写姓名！&quot; Display=&quot;Dynamic&quot; runat=&quot;server&quot;&gt;&lt;/asp:RequiredFieldValidator&gt;<br/>&nbsp;&nbsp;&lt;/div&gt;<br/>&nbsp;&nbsp;&lt;div&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&lt;asp:Button ID=&quot;btnSubmit&quot; Text=&quot;提交&quot; runat=&quot;server&quot; /&gt;<br/>&nbsp;&nbsp;&lt;/div&gt;<br/>&lt;/form&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;这是一个HTML片段，其中有一个RequiredFieldValidator控件用于检查是否已填写姓名。下面是点击按钮时执行的服务器端代码:<br/><br/>protected void btnSubmit_Click(object sender, EventArgs e)<br/>&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;if (Page.IsValid) //注意：不要遗漏对Page.IsValid属性的判断<br/>&nbsp;&nbsp;&nbsp;&nbsp;&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Response.Write(&quot;你的名字是：&quot; + txtName.Text);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&#125;<br/>&#125;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;其中，要特别注意对Page.IsValid属性的判断，只有页面中所有验证控件对数据的验证都成功时，Page.IsValid属性才为True，这代表提交的数据为有效数据，可以进入下一步操作。<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4.跳过表单验证<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;在某些情况下，我们需要跳过表单中所有控件的验证，然而在另外一些情况下，我们却希望有选择的触发表单中某些控件的验证功能。分别来看看这两种情况：<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;a.跳过所有验证<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;假设有个表单，其中除了各种数据录入控件外还有两个按钮，一个是提交按钮，另一个是取消按钮，同时表单中还有一些数据验证控件。我们希望当点击取消按钮的时候无需验证表单中数据的有效性，而是直接将页面提交至服务器并将其重定向到某个指定页面。<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;要实现这个功能，可以利用按钮控件(包括Button、LinkButton、ImageButton控件)的CausesValidation属性，将该属性设为false即可跳过表单中的所有验证。<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;b.触发某些验证<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;假设有个表单，被划分成两个功能区，一个用于用户登录，另一个用于用户注册，我们希望当点击登录按钮时只触发登录区的数据验证，当点击注册按钮时只触发注册区的数据验证。<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;解决办法是将相关的数据验证控件和数据提交控件(按钮控件)加入同一个验证组，这一点是通过将各相关控件的ValidationGroup属性设为相同的值来实现的。<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;5.保持滚动条位置<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;假设有个页面，其中以列表形式显示了一些数据记录，每次编辑其中的记录时都需要向服务器提交页面，为了提供良好的用户体验，我们希望每次编辑一条记录并保存后，滚动条位置都能保持不变。传统的做法是每次提交页面时将当前滚动条所在位置信息按某种方式(Hidden字段或QueryString)传给服务器端，当页面返回客户端时，由服务器端根据传入的位置信息以JavaScript形式重新设置滚动条位置。<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;如果通过ASP.NET来实现这一功能将变得非常简单，只需在Page指令中加入MaintainScrollPositionOnPostback=&quot;true&quot;属性即可：<br/><br/><br/>&lt;%@ Page Language=&quot;C#&quot; MaintainScrollPositionOnPostback=&quot;true&quot;&gt;<br/> <br/>&nbsp;&nbsp;&nbsp;&nbsp; 6.禁用不必要的ViewState<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;在ASP.NET的运行机制中，ViewState起着重要的作用。ViewState经过编码后存入表单Hidden字段，每当页面回传至服务器时再进行解码。因此，ViewState的使用会带来两个问题：带宽的占用以及计算资源的消耗。好在不是所有控件都需要启用ViewState,我们完全可以禁用不必要的ViewState。<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ViewState默认是开启的，需要手动关闭：<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;a.禁用页面ViewState<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;在Page指令中加入EnableViewState=&quot;false&quot;属性即可：<br/><br/><br/>&lt;%@ Page Language=&quot;C#&quot; EnableViewState=&quot;false&quot;&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;加入这个属性后，整个页面以及其中的所有控件都将无法使用ViewState，因此需谨慎使用。<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;b.禁用控件ViewState<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;这是推荐的方式，将控件的EnableViewState属性设为False即可禁用其ViewState，这里有个简单的窍门：<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;如果某个控件的状态不能由操作者改变，则可以禁用其ViewState。最典型的莫过于Label控件了，只能显示信息，无法操作。<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;但TextBox、DorpDownList等控件的状态是可以改变的(通过输入、选择等操作)，因此保留他们的ViewState还是有用的。<br/><br/>
]]>
</description>
</item><item>
<link>http://www.ywqi.com/read.php/210.htm</link>
<title><![CDATA[ C# StreamReader 和 StreamWriter 读取和写入汉字出现乱码的解决方法。]]></title> 
<author>bgaidu &lt;admin@yourname.com&gt;</author>
<category><![CDATA[winform]]></category>
<pubDate>Wed, 24 Dec 2008 08:35:43 +0000</pubDate> 
<guid>http://www.ywqi.com/read.php/210.htm</guid> 
<description>
<![CDATA[ 
	注意：汉字使用GB2312编码<br/><br/>测试页面代码：<br/><br/>using System;<br/>using System.Collections.Generic;<br/>using System.Text;<br/><br/>namespace StreamReaderAndStreamWriter<br/>&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;class Program<br/>&nbsp;&nbsp;&nbsp;&nbsp;&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;static void Main(string[] args)<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Console.WriteLine(&quot;读取文本文件内容&quot;);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Console.Write(&quot;输入文本文件所在目录：&quot;);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;string sBaseFile = Console.ReadLine();<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;StreamReaderAndStreamWriter.ReadFile(sBaseFile);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Console.WriteLine(&quot;追加文件&quot;);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;StreamReaderAndStreamWriter.AppendText(sBaseFile);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#125;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&#125;<br/>&#125;<br/>类页面代码：<br/><br/>using System;<br/>using System.Collections.Generic;<br/>using System.Text;<br/>using System.IO;<br/><br/>namespace StreamReaderAndStreamWriter<br/>&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;class StreamReaderAndStreamWriter<br/>&nbsp;&nbsp;&nbsp;&nbsp;&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//读取文本文件<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;public static void ReadFile(string sFile)<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (File.Exists(sFile))<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//获取一个文件流对象，用于读写文件<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;FileStream fs = File.OpenRead(sFile);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//获取一个指向文件流的流读取器<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;StreamReader sr = new StreamReader(fs,Encoding.GetEncoding(&quot;gb2312&quot;));//以gb2312编码读取文本文件中的汉字，要不然，读取的内容中如果有汉字，则显示为乱码。<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//读取所有文本内容<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;string data = sr.ReadToEnd();<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//关闭对象，释放资源<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sr.Close();<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fs.Close();<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Console.WriteLine(string.Format(&quot;读取文件&gt;&gt;&#123;0&#125;&quot;, sFile));<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Console.WriteLine(data);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#125;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;else<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Console.WriteLine(string.Format(&quot;&#123;0&#125;不存在&quot;,sFile));<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#125;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//追加文本文件<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;public static void AppendText(string sFile)<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (File.Exists(sFile))<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//编辑文本文件<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Console.WriteLine(&quot;输入写入内容&quot;);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;string swrite = Console.ReadLine();<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//获取一个指向文件流的流编辑器<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;StreamWriter sw = new StreamWriter(sFile, true, Encoding.GetEncoding(&quot;gb2312&quot;));//这里很重要，看一下这个StreamWriter()格式就一目了然了，sFile声明了文本对象；true声明了可以进行Appedn；Encoding.GetEncoding(&quot;gb2312&quot;)声明了一GB2312编码向文本文件写入内容，这样就可以避免写入汉字出现乱码。<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sw.Write(swrite);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//关闭对象，释放资源<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sw.Close();<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//fs.Close();<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Console.WriteLine(&quot;向&#123;0&#125;中追加文件&quot;,sFile);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#125;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;else<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Console.WriteLine(&quot;&#123;0&#125;不存在&quot;,sFile);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#125;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&#125;<br/>&#125;<br/><br/><br/>
]]>
</description>
</item>
</channel>
</rss>