手记

细说ASP.NET Windows身份认证

阅读目录

  • 开始

  • 认识ASP.NET Windows身份认证

  • 访问 Active Directory

  • 在ASP.NET中访问Active Directory

  • 使用Active Directory验证用户身份

  • 安全上下文与用户模拟

  • 在IIS中配置Windows身份认证

  • 关于浏览器的登录对话框问题

  • 在客户端代码中访问Windows身份认证的页面

上篇博客我谈到了一些关于ASP.NET Forms身份认证方面的话题,这次的博客将主要介绍ASP.NET Windows身份认证。

Forms身份认证虽然使用广泛,不过,如果是在 Windows Active Directory 的环境中使用ASP.NET,那么使用Windows身份认证也会比较方便。方便性表现为:我们不用再设计登录页面,不用编写登录验证逻辑。而且使用Windows身份认证会有更好的安全保障。

回到顶部

认识ASP.NET Windows身份认证

要使用Windows身份认证模式,需要在web.config设置:

<authentication mode="Windows" />

Windows身份认证做为ASP.NET的默认认证方式,与Forms身份认证在许多基础方面是一样的。上篇博客我说过:我认为ASP.NET的身份认证的最核心部分其实就是HttpContext.User这个属性所指向的对象。在接下来的部分,我将着重分析这个对象在二种身份认证中有什么差别。

在ASP.NET身份认证过程中,IPrincipal和IIdentity这二个接口有着非常重要的作用。前者定义用户对象的基本功能,后者定义标识对象的基本功能,不同的身份认证方式得到的这二个接口的实例也是不同的。

ASP.NET Windows身份认证是由WindowsAuthenticationModule实现的。WindowsAuthenticationModule在ASP.NET管线的AuthenticateRequest事件中,使用从IIS传递到ASP.NET的Windows访问令牌(Token)创建一个WindowsIdentity对象,Token通过调用context.WorkerRequest.GetUserToken()获得,然后再根据WindowsIdentity 对象创建WindowsPrincipal对象,然后把它赋值给HttpContext.User。

在Forms身份认证中,我们需要创建登录页面,让用户提交用户名和密码,然后检查用户名和密码的正确性,接下来创建一个包含FormsAuthenticationTicket对象的登录Cookie供后续请求使用。FormsAuthenticationModule在ASP.NET管线的AuthenticateRequest事件中,解析登录Cookie并创建一个包含FormsIdentity的GenericPrincipal对象,然后把它赋值给HttpContext.User。

上面二段话简单了概括了二种身份认证方式的工作方式。
我们可以发现它们存在以下差别:
1. Forms身份认证需要Cookie表示登录状态,Windows身份认证则依赖于IIS
2. Windows身份认证不需要我们设计登录页面,不用编写登录验证逻辑,因此更容易使用。

在授权阶段,UrlAuthorizationModule仍然会根据当前用户检查将要访问的资源是否得到许可。接下来,FileAuthorizationModule检查 HttpContext.User.Identity 属性中的 IIdentity 对象是否是 WindowsIdentity 类的一个实例。如果 IIdentity 对象不是 WindowsIdentity 类的一个实例,则 FileAuthorizationModule 类停止处理。如果存在 WindowsIdentity 类的一个实例,则 FileAuthorizationModule 类调用 AccessCheck Win32 函数(通过 P/Invoke)来确定是否授权经过身份验证的客户端访问请求的文件。如果该文件的安全描述符的随机访问控制列表 (DACL) 中至少包含一个 Read 访问控制项 (ACE),则允许该请求继续。否则,FileAuthorizationModule 类调用 HttpApplication.CompleteRequest 方法并将状态码 401 返回到客户端。

在Windows身份认证中,验证工作主要是由IIS实现的,WindowsAuthenticationModule其实只是负责创建WindowsPrincipal和WindowsIdentity而已。顺便介绍一下:Windows 身份验证又分为“NTLM 身份验证”和“Kerberos v5 身份验证”二种,关于这二种Windows身份认证的更多说明可查看MSDN技术文章:解释:ASP.NET 2.0 中的 Windows 身份验证。在我看来,IIS最终使用哪种Windows身份认证方式并不影响我们的开发过程,因此本文不会讨论这个话题。

根据我的实际经验来看,使用Windows身份认证时,主要的开发工作将是根据登录名从Active Directory获取用户信息。因为,此时不需要我们再设计登录过程,IIS与ASP.NET已经为我们准备好了WindowsPrincipal和WindowsIdentity这二个与用户身份相关的对象。

回到顶部

访问 Active Directory

我们通常使用LDAP协议来访问Active Directory,在.net framework中提供了DirectoryEntry和DirectorySearcher这二个类型让我们可以方便地从托管代码中访问 Active Directory 域服务。

如果我们要在"test.corp”这个域中搜索某个用户信息,我们可以使用下面的语句构造一个DirectoryEntry对象:

DirectoryEntry entry = new DirectoryEntry("LDAP://test.corp");

在这段代码中,我采用硬编码的方式把域名写进了代码。
我们如何知道当前电脑所使用的是哪个域名呢?
答案是:查看“我的电脑”的属性对话框:

注意:这个域名不一定与System.Environment.UserDomainName相同。

除了可以查看“我的电脑”的属性对话框外,我们还可以使用代码的方式获取当前电脑所使用的域名:

private static string GetDomainName(){    // 注意:这段代码需要在Windows XP及较新版本的操作系统中才能正常运行。    SelectQuery query = new SelectQuery("Win32_ComputerSystem");    using( ManagementObjectSearcher searcher = new ManagementObjectSearcher(query) ) {        foreach( ManagementObject mo in searcher.Get() ) {            if( (bool)mo["partofdomain"] )                return mo["domain"].ToString();        }    }    return null;}

当构造了DirectorySearcher对象后,我们便可以使用DirectorySearcher来执行对Active Directory的搜索。
我们可以使用下面的步骤来执行搜索:
1. 设置 DirectorySearcher.Filter 指示LDAP格式筛选器,这是一个字符串。
2. 多次调用PropertiesToLoad.Add() 设置搜索过程中要检索的属性列表。
3. 调用FindOne() 方法获取搜索结果。

下面的代码演示了如何从Active Directory中搜索登录名为“fl45”的用户信息:

static void Main(string[] args){    Console.WriteLine(Environment.UserDomainName);    Console.WriteLine(Environment.UserName);    Console.WriteLine("------------------------------------------------");    ShowUserInfo("fl45", GetDomainName());}private static string AllProperties = "name,givenName,samaccountname,mail";public static void ShowUserInfo(string loginName, string domainName){    if( string.IsNullOrEmpty(loginName) || string.IsNullOrEmpty(domainName) )        return;    string[] properties = AllProperties.Split(new char[] { '\r', '\n', ',' },                         StringSplitOptions.RemoveEmptyEntries);    try {        DirectoryEntry entry = new DirectoryEntry("LDAP://" + domainName);        DirectorySearcher search = new DirectorySearcher(entry);        search.Filter = "(samaccountname=" + loginName + ")";        foreach( string p in properties )            search.PropertiesToLoad.Add(p);        SearchResult result = search.FindOne();        if( result != null ) {            foreach( string p in properties ) {                ResultPropertyValueCollection collection = result.Properties[p];                for( int i = 0; i < collection.Count; i++ )                    Console.WriteLine(p + ": " + collection[i]);            }        }    }    catch( Exception ex ) {        Console.WriteLine(ex.ToString());    }}

结果如下:

在前面的代码,我在搜索Active Directory时,只搜索了"name,givenName,samaccountname,mail"这4个属性。然而,LDAP还支持更多的属性,我们可以使用下面的代码查看更多的用户信息:

        private static string AllProperties = @"homemdbdistinguishednamecountrycodecnlastlogoffmailnicknamedscorepropagationdatamsexchhomeservernamemsexchmailboxsecuritydescriptormsexchalobjectversionusncreatedobjectguidwhenchangedmemberofmsexchuseraccountcontrolaccountexpiresdisplaynameprimarygroupidbadpwdcountobjectclassinstancetypeobjectcategorysamaccounttypewhencreatedlastlogonuseraccountcontrolphysicaldeliveryofficenamesamaccountnameusercertificategivennamemailuserparametersadspathhomemtamsexchmailboxguidpwdlastsetlogoncountcodepagenameusnchangedlegacyexchangednproxyaddressesdepartmentuserprincipalnamebadpasswordtimeobjectsidsnmdbusedefaultstelephonenumbershowinaddressbookmsexchpoliciesincludedtextencodedoraddresslastlogontimestampcompany";

回到顶部

在ASP.NET中访问Active Directory

前面我在一个控制台程序中演示了访问Active Directory的方法,通过示例我们可以看到:在代码中,我用Environment.UserName就可以得到当前用户的登录名。然而,如果是在ASP.NET程序中,访问Environment.UserName就很有可能得不到真正用户登录名。因为:Environment.UserName是使用WIN32API中的GetUserName获取线程相关的用户名,但ASP.NET运行在IIS中,线程相关的用户名就不一定是客户端的用户名了。不过,ASP.NET可以模拟用户方式运行,通过这种方式才可以得到正确的结果。关于“模拟”的话题在本文的后面部分有说明。

在ASP.NET中,为了能可靠的获取登录用户的登录名,我们可以使用下面的代码:

/// <summary>/// 根据指定的HttpContext对象,获取登录名。/// </summary>/// <param name="context"></param>/// <returns></returns>public static string GetUserLoginName(HttpContext context){    if( context == null )        return null;    if( context.Request.IsAuthenticated == false )        return null;    string userName = context.User.Identity.Name;    // 此时userName的格式为:UserDomainName\LoginName    // 我们只需要后面的LoginName就可以了。    string[] array = userName.Split(new char[] { '\\' }, StringSplitOptions.RemoveEmptyEntries);    if( array.Length == 2 )        return array[1];    return null;}

在ASP.NET中使用Windows身份认证时,IIS和WindowsAuthenticationModule已经做了许多验证用户的相关工作,虽然我们可以使用前面的代码获取到用户的登录名,但用户的其它信息即需要我们自己来获取。在实际使用Windows身份认证时,我们要做的事:基本上就是从Active Directory中根据用户的登录名获取所需的各种信息。

比如:我的程序在运行时,还需要使用以下与用户相关的信息:

public sealed class UserInfo{    public string GivenName;    public string FullName;    public string Email;}

那么,我们可以使用这样的代码来获取所需的用户信息:

public static class UserHelper{    /// <summary>    /// 活动目录中的搜索路径,也可根据实际情况来修改这个值。    /// </summary>    public static string DirectoryPath = "LDAP://" + GetDomainName();    /// <summary>    /// 获取与指定HttpContext相关的用户信息    /// </summary>    /// <param name="context"></param>    /// <returns></returns>    public static UserInfo GetCurrentUserInfo(HttpContext context)    {        string loginName = GetUserLoginName(context);        if( string.IsNullOrEmpty(loginName) )            return null;        return GetUserInfoByLoginName(loginName);    }    /// <summary>    /// 根据指定的HttpContext对象,获取登录名。    /// </summary>    /// <param name="context"></param>    /// <returns></returns>    public static string GetUserLoginName(HttpContext context)    {        if( context == null )            return null;        if( context.Request.IsAuthenticated == false )            return null;        string userName = context.User.Identity.Name;        // 此时userName的格式为:UserDomainName\LoginName        // 我们只需要后面的LoginName就可以了。        string[] array = userName.Split(new char[] { '\\' }, StringSplitOptions.RemoveEmptyEntries);        if( array.Length == 2 )            return array[1];        return null;    }        /// <summary>    /// 根据登录名查询活动目录,获取用户信息。    /// </summary>    /// <param name="loginName"></param>    /// <returns></returns>    public static UserInfo GetUserInfoByLoginName(string loginName)    {        if( string.IsNullOrEmpty(loginName) )            return null;        // 下面的代码将根据登录名查询用户在AD中的信息。        // 为了提高性能,可以在此处增加一个缓存容器(Dictionary or Hashtable)。        try {            DirectoryEntry entry = new DirectoryEntry(DirectoryPath);            DirectorySearcher search = new DirectorySearcher(entry);            search.Filter = "(SAMAccountName=" + loginName + ")";            search.PropertiesToLoad.Add("givenName");            search.PropertiesToLoad.Add("cn");            search.PropertiesToLoad.Add("mail");            // 如果还需要从AD中获取其它的用户信息,请参考ActiveDirectoryDEMO            SearchResult result = search.FindOne();            if( result != null ) {                UserInfo info = new UserInfo();                info.GivenName = result.Properties["givenName"][0].ToString();                info.FullName = result.Properties["cn"][0].ToString();                info.Email = result.Properties["mail"][0].ToString();                return info;            }        }        catch {            // 如果需要记录异常,请在此处添加代码。        }        return null;    }    private static string GetDomainName()    {        // 注意:这段代码需要在Windows XP及较新版本的操作系统中才能正常运行。        SelectQuery query = new SelectQuery("Win32_ComputerSystem");        using( ManagementObjectSearcher searcher = new ManagementObjectSearcher(query) ) {            foreach( ManagementObject mo in searcher.Get() ) {                if( (bool)mo["partofdomain"] )                    return mo["domain"].ToString();            }        }        return null;    }}

使用UserHelper的页面代码:

<html xmlns="http://www.w3.org/1999/xhtml"><head>    <title>WindowsAuthentication DEMO  - http://www.cnblogs.com/fish-li/</title></head><body><% if( Request.IsAuthenticated ) { %>    当前登录全名:<%= Context.User.Identity.Name.HtmlEncode()%> <br />        <% var user = UserHelper.GetCurrentUserInfo(Context); %>    <% if( user != null ) { %>        用户短名:<%= user.GivenName.HtmlEncode()%> <br />        用户全名:<%= user.FullName.HtmlEncode() %> <br />        邮箱地址:<%= user.Email.HtmlEncode() %>    <% } %>    <% } else { %>    当前用户还未登录。<% } %></body></html>

程序运行的效果如下:

另外,还可以从Active Directory查询一个叫做memberof的属性(它与Windows用户组无关),有时候可以用它区分用户,设计与权限相关的操作。

在设计数据持久化的表结构时,由于此时没有“用户表”,那么我们可以直接保存用户的登录名。剩下的开发工作就与Forms身份认证没有太多的差别了。

回到顶部

使用Active Directory验证用户身份

前面介绍了ASP.NET Windows身份认证,在这种方式下,IIS和WindowsAuthenticationModule为我们实现了用户身份认证的过程。然而,有时可能由于各种原因,需要我们以编程的方式使用Active Directory验证用户身份,比如:在WinForm程序,或者其它的验证逻辑。

我们不仅可以从Active Directory中查询用户信息,也可以用它来实现验证用户身份,这样便可以实现自己的登录验证逻辑。

不管是如何使用Active Directory,我们都需要使用DirectoryEntry和DirectorySearcher这二个对象。DirectoryEntry还提供一个构造函数可让我们输入用户名和密码:

// 摘要://     初始化 System.DirectoryServices.DirectoryEntry 类的新实例。//// 参数://   Password://     在对客户端进行身份验证时使用的密码。DirectoryEntry.Password 属性初始化为该值。////   username://     在对客户端进行身份验证时使用的用户名。DirectoryEntry.Username 属性初始化为该值。////   Path://     此 DirectoryEntry 的路径。DirectoryEntry.Path 属性初始化为该值。public DirectoryEntry(string path, string username, string password);

要实现自己的登录检查,就需要使用这个构造函数。
以下是我写用WinForm写的一个登录检查的示例:

private void btnLogin_Click(object sender, EventArgs e){    if( txtUsername.Text.Length == 0 || txtPassword.Text.Length == 0 ) {        MessageBox.Show("用户名或者密码不能为空。", this.Text, MessageBoxButtons.OK, MessageBoxIcon.Warning);        return;    }    string ldapPath = "LDAP://" + GetDomainName();    string domainAndUsername = Environment.UserDomainName + "\\" + txtUsername.Text;    DirectoryEntry entry = new DirectoryEntry(ldapPath, domainAndUsername, txtPassword.Text);    DirectorySearcher search = new DirectorySearcher(entry);    try {        SearchResult result = search.FindOne();        MessageBox.Show("登录成功。", this.Text, MessageBoxButtons.OK, MessageBoxIcon.Information);    }    catch( Exception ex ) {        // 如果用户名或者密码不正确,也会抛出异常。        MessageBox.Show(ex.Message, this.Text, MessageBoxButtons.OK, MessageBoxIcon.Stop);    }}

程序运行的效果如下:

回到顶部

安全上下文与用户模拟

在ASP.NET Windows身份认证环境中,与用户相关的安全上下文对象保存在HttpContext.User属性中,是一个类型为WindowsPrincipal的对象,我们还可以访问HttpContext.User.Identity来获取经过身份认证的用户标识,它是一个WindowsIdentity类型的对象。

在.NET Framework中,我们可以通过WindowsIdentity.GetCurrent()获取与当前线程相关的WindowsIdentity对象,这种方法获取的是当前运行的Win32线程的安全上下文标识。由于ASP.NET运行在IIS进程中,因此ASP.NET线程的安全标识其实是从IIS的进程中继承的,所以此时用二种方法得到的WindowsIdentity对象其实是不同的。

在Windows操作系统中,许多权限检查都是基于Win32线程的安全上下文标识,于是前面所说的二种WindowsIdentity对象会造成编程模型的不一致问题,为了解决这个问题,ASP.NET提供了“模拟”功能,允许线程以特定的Windows帐户的安全上下文来访问资源。

为了能更好的理解模拟的功能,我准备了一个示例(ShowWindowsIdentity.ashx):

public class ShowWindowsIdentity : IHttpHandler {        public void ProcessRequest (HttpContext context) {        // 要观察【模拟】的影响,        // 可以启用,禁止web.config中的设置:<identity impersonate="true"/>                context.Response.ContentType = "text/plain";        context.Response.Write(Environment.UserDomainName + "\\" + Environment.UserName + "\r\n");                WindowsPrincipal winPrincipal = (WindowsPrincipal)HttpContext.Current.User;        context.Response.Write(string.Format("HttpContext.Current.User.Identity: {0}, {1}\r\n",                 winPrincipal.Identity.AuthenticationType, winPrincipal.Identity.Name));                WindowsPrincipal winPrincipal2 = (WindowsPrincipal)Thread.CurrentPrincipal;        context.Response.Write(string.Format("Thread.CurrentPrincipal.Identity: {0}, {1}\r\n",                winPrincipal2.Identity.AuthenticationType, winPrincipal2.Identity.Name));        WindowsIdentity winId = WindowsIdentity.GetCurrent();        context.Response.Write(string.Format("WindowsIdentity.GetCurrent(): {0}, {1}",                winId.AuthenticationType, winId.Name));    }

首先,在web.config中设置:

<authentication mode="Windows" />

注意:要把网站部署在IIS中,否则看不出效果。

此时,访问ShowWindowsIdentity.ashx,将看到如下图所示的结果:

现在修改一下web.config中设置:(注意:后面加了一句配置

<authentication mode="Windows" /><identity impersonate="true"/>

此时,访问ShowWindowsIdentity.ashx,将看到如下图所示的结果:

说明:
1. FISH-SRV2003是我的计算机名。它在一个没有域的环境中。
2. fish-li是我的一个Windows帐号的登录名。
3. 网站部署在IIS6中,进程以NETWORK SERVICE帐号运行。
4. 打开网页时,我输入的用户名是fish-li

前面二张图片的差异之处其实也就是ASP.NET的“模拟”所发挥的功能。

关于模拟,我想说四点:
1. 在ASP.NET中,我们应该访问HttpContext.User.Identity获取当前用户标识,那么就不存在问题(此时可以不需要模拟),例如FileAuthorizationModule就是这样处理的。
2. 模拟只是在ASP.NET应用程序访问Windows系统资源时需要应用Windows的安全检查功能才会有用。
3. Forms身份认证也能配置模拟功能,但只能模拟一个Windows帐户。
4. 绝大多数情况下是不需要模拟的。

回到顶部

在IIS中配置Windows身份认证

与使用Forms身份认证的程序不同,使用Windows身份认证的程序需要额外的配置步骤。这个小节将主要介绍在IIS中配置Windows身份认证,我将常用的IIS6和IIS7.5为例分别介绍这些配置。

IIS6的配置 请参考下图:


IIS7.5的配置 请参考下图:

注意:Windows身份认证是需要安装的,方法请参考下图:

回到顶部

关于浏览器的登录对话框问题

当我们用浏览器访问一个使用Windows身份认证的网站时,浏览器都会弹出一个对话框(左IE,右Safari):

此时,要求我们输入Windows的登录帐号,然后交给IIS验证身份。

首次弹出这个对话框很正常:因为程序要验证用户的身份。
然而,每次关闭浏览器下次重新打开页面时,又会出现此对话框,此时感觉就很不方便了。
虽然有些浏览器能记住用户名和密码,但我发现FireFox,Opera,Chrome仍然会弹出这个对话框,等待我们点击确定,只有Safari才不会打扰用户直接打开网页。IE的那个“记住我的密码”复选框完全是个摆设,它根本不会记住密码!

因此,我所试过的所有浏览器中,只有Safari是最人性化的。
虽然在默认情况下,虽然IE不会记住密码,每次都需要再次输入。
不过,IE却可以支持不提示用户输入登录帐号而直接打开网页,此时IE将使用用户的当前Windows登录帐号传递给IIS验证身份。

要让IE打开一个Windows身份认证的网站不提示登录对话框,必须满足以下条件:
1. 必须在 IIS 的 Web 站点属性中启用 Windows 集成身份验证。
2. 客户端和Web服务器都必须在基于Microsoft Windows的同一个域内。
3. Internet Explorer 必须把所请求的 URL 视为 Intranet(本地)。
4. Internet Explorer 的 Intranet 区域的安全性设置必须设为“只在 Intranet 区域自动登录”。
5. 请求Web页的用户必须具有访问该Web页以及该Web页中引用的所有对象的适当的文件系统(NTFS)权限。
6. 用户必须用域帐号登录到Windows 。

在这几个条件中,如果网站是在一个Windows域中运行,除了第3条可能不满足外,其它条件应该都容易满足(第4条是默认值)。因此,要让IE不提示输入登录帐号,只要确保第3条满足就可以了。下面的图片演示了如何完成这个配置:(注意:配置方法也适合用域名访问的情况)


另外,除了在IE中设置Intranet外,还可以在访问网站时,用计算机名代替IP地址或者域名,那么IE始终认为是在访问Intranet内的网站,此时也不会弹出登录对话框。

在此,我想再啰嗦三句:
1. IE在集成Windows身份认证时,虽然不提示登录对话框,但是不表示不安全,它会自动传递登录凭据。
2. 这种行为只有IE才能支持。(其它的浏览器只是会记住密码,在实现上其实是不一样的。)
3. 集成Windows身份认证,也只适合在Intranet的环境中使用。

回到顶部

在客户端代码中访问Windows身份认证的页面

在上篇博客中,我演示了如何用代码访问一个使用Forms身份认证的网站中的受限页面,方法是使用CookieContainer对象接收服务端生的登录Cookie。然而,在Windows身份认证的网站中,身份验证的过程发生在IIS中,而且根本不使用Cookie保存登录状态,而是需要在请求时发送必要的身份验证信息。

在使用代码做为客户端访问Web服务器时,我们仍然需要使用HttpWebRequest对象。为了能让HttpWebRequest在访问IIS时发送必要的身份验证信息,HttpWebRequest提供二个属性都可以完成这个功能:

// 获取或设置请求的身份验证信息。//// 返回结果://     包含与该请求关联的身份验证凭据的 System.Net.ICredentials。默认为 null。public override ICredentials Credentials { get; set; }// 获取或设置一个 System.Boolean 值,该值控制默认凭据是否随请求一起发送。//// 返回结果://     如果使用默认凭据,则为 true;否则为 false。默认值为 false。public override bool UseDefaultCredentials { get; set; }

下面是我准备的完整的示例代码(注意代码中的注释)

static void Main(string[] args){    try {        // 请把WindowsAuthWebSite1这个网站部署在IIS中,        // 开启Windows认证方式,并禁止匿名用户访问。        // 然后修改下面的访问地址。        HttpWebRequest request =             (HttpWebRequest)WebRequest.Create("http://localhost:33445/Default.aspx");        // 下面三行代码,启用任意一行都是可以的。        request.UseDefaultCredentials = true;        //request.Credentials = CredentialCache.DefaultCredentials;        //request.Credentials = CredentialCache.DefaultNetworkCredentials;        // 如果上面的三行代码全被注释了,那么将会看到401的异常信息。        using( HttpWebResponse response = (HttpWebResponse)request.GetResponse() ) {            using( StreamReader sr = new StreamReader(response.GetResponseStream()) ) {                Console.WriteLine(sr.ReadToEnd());            }        }    }    catch( WebException wex ) {        Console.WriteLine("=====================================");        Console.WriteLine("异常发生了。");        Console.WriteLine("=====================================");        Console.WriteLine(wex.Message);    }}

其实关键部分还是设置UseDefaultCredentials或者Credentials,代码中的三种方法是有效的。
这三种方法的差别:
1. Credentials = CredentialCache.DefaultCredentials; 表示在发送请求会带上当前用户的身份验证凭据。
2. UseDefaultCredentials = true; 此方法在内部会调用前面的方法,因此与前面的方法是一样的。
3. Credentials = CredentialCache.DefaultNetworkCredentials; 是在.NET 2.0中引用的新方法。

关于DefaultCredentials和DefaultNetworkCredentials的更多差别,请看我整理的表格:

Credentials属性申明类型实例类型.NET支持版本
DefaultCredentialsICredentialsSystemNetworkCredential从1.0开始
DefaultNetworkCredentialsNetworkCredentialSystemNetworkCredential从2.0开始

三个类型的继承关系:
1. NetworkCredential实现了ICredentials接口,
2. SystemNetworkCredential继承自NetworkCredential。


在结束这篇博客之前,我想我应该感谢新蛋。
在新蛋的网络环境中,让我学会了使用Windows身份认证。
除了感谢之外,我现在还特别怀念 fl45 这个登录名......


点击此处下载示例代码

如果,您认为阅读这篇博客让您有些收获,不妨点击一下右下角的【推荐】按钮。
如果,您希望更容易地发现我的新博客,不妨点击一下右下角的【关注 Fish Li】。
因为,我的写作热情也离不开您的肯定支持。

感谢您的阅读,如果您对我的博客所讲述的内容有兴趣,请继续关注我的后续博客,我是Fish Li 。

0人推荐
随时随地看视频
慕课网APP