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

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

认识ASP.NET Windows身份认证

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

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对象:DirectoryEntryentry =newDirectoryEntry("LDAP://test.corp");

在这段代码中,我采用硬编码的方式把域名写进了代码。

我们如何知道当前电脑所使用的是哪个域名呢?

答案是:查看“我的电脑”的属性对话框:

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

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

{

// 注意:这段代码需要在Windows XP及较新版本的操作系统中才能正常运行。SelectQueryquery =newSelectQuery("Win32_ComputerSystem");

using( ManagementObjectSearchersearcher =newManagementObjectSearcher(query) ) {

foreach( ManagementObjectmo insearcher.Get() ) {

if( (bool)mo["partofdomain"] )

returnmo["domain"].ToString();

}

}

return null;

}

当构造了DirectorySearcher对象后,我们便可以使用DirectorySearcher来执行对Active Directory的搜索。

我们可以使用下面的步骤来执行搜索:

1. 设置 DirectorySearcher.Filter 指示LDAP格式筛选器,这是一个字符串。

2. 多次调用PropertiesToLoad.Add() 设置搜索过程中要检索的属性列表。

3. 调用FindOne() 方法获取搜索结果。

下面的代码演示了如何从Active Directory中搜索登录名为“fl45”的用户信息:static voidMain(string[] args)

{

Console.WriteLine(Environment.UserDomainName);

Console.WriteLine(Environment.UserName);

Console.WriteLine("------------------------------------------------");

ShowUserInfo("fl45", GetDomainName());

}

private static stringAllProperties ="name,givenName,samaccountname,mail";

public static voidShowUserInfo(stringloginName, stringdomainName)

{

if( string.IsNullOrEmpty(loginName) ||string.IsNullOrEmpty(domainName) )

return;

string[] properties =AllProperties.Split(new char[] { '\r', '\n', ','},

StringSplitOptions.RemoveEmptyEntries);

try{

DirectoryEntryentry =newDirectoryEntry("LDAP://"+domainName);

DirectorySearchersearch =newDirectorySearcher(entry);

search.Filter ="(samaccountname="+loginName +")";

foreach( stringp inproperties )

search.PropertiesToLoad.Add(p);

SearchResultresult =search.FindOne();

if( result !=null) {

foreach( stringp inproperties ) {

ResultPropertyValueCollectioncollection =result.Properties[p];

for( inti =0; i

Console.WriteLine(p +": "+collection[i]);

}

}

}

catch( Exceptionex ) {

Console.WriteLine(ex.ToString());

}

}

结果如下:

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

homemdb

distinguishedname

countrycode

cn

lastlogoff

mailnickname

dscorepropagationdata

msexchhomeservername

msexchmailboxsecuritydescriptor

msexchalobjectversion

usncreated

objectguid

whenchanged

memberof

msexchuseraccountcontrol

accountexpires

displayname

primarygroupid

badpwdcount

objectclass

instancetype

objectcategory

samaccounttype

whencreated

lastlogon

useraccountcontrol

physicaldeliveryofficename

samaccountname

usercertificate

givenname

mail

userparameters

adspath

homemta

msexchmailboxguid

pwdlastset

logoncount

codepage

name

usnchanged

legacyexchangedn

proxyaddresses

department

userprincipalname

badpasswordtime

objectsid

sn

mdbusedefaults

telephonenumber

showinaddressbook

msexchpoliciesincluded

textencodedoraddress

lastlogontimestamp

company

";

在ASP.NET中访问Active Directory

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

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

///根据指定的HttpContext对象,获取登录名。///

///

/// public static stringGetUserLoginName(HttpContextcontext)

{

if( context ==null)

return null;

if( context.Request.IsAuthenticated ==false)

return null;

stringuserName =context.User.Identity.Name;

// 此时userName的格式为:UserDomainName\LoginName

// 我们只需要后面的LoginName就可以了。string[] array =userName.Split(new char[] { '\\'}, StringSplitOptions.RemoveEmptyEntries);

if( array.Length ==2)

returnarray[1];

return null;

}

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

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

public stringGivenName;

public stringFullName;

public stringEmail;

}

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

///

///活动目录中的搜索路径,也可根据实际情况来修改这个值。/// public static stringDirectoryPath ="LDAP://"+GetDomainName();

///

///获取与指定HttpContext相关的用户信息///

///

/// public staticUserInfoGetCurrentUserInfo(HttpContextcontext)

{

stringloginName =GetUserLoginName(context);

if( string.IsNullOrEmpty(loginName) )

return null;

returnGetUserInfoByLoginName(loginName);

}

///

///根据指定的HttpContext对象,获取登录名。///

///

/// public static stringGetUserLoginName(HttpContextcontext)

{

if( context ==null)

return null;

if( context.Request.IsAuthenticated ==false)

return null;

stringuserName =context.User.Identity.Name;

// 此时userName的格式为:UserDomainName\LoginName

// 我们只需要后面的LoginName就可以了。string[] array =userName.Split(new char[] { '\\'}, StringSplitOptions.RemoveEmptyEntries);

if( array.Length ==2)

returnarray[1];

return null;

}

///

///根据登录名查询活动目录,获取用户信息。///

///

/// public staticUserInfoGetUserInfoByLoginName(stringloginName)

{

if( string.IsNullOrEmpty(loginName) )

return null;

// 下面的代码将根据登录名查询用户在AD中的信息。

// 为了提高性能,可以在此处增加一个缓存容器(Dictionary or Hashtable)。try{

DirectoryEntryentry =newDirectoryEntry(DirectoryPath);

DirectorySearchersearch =newDirectorySearcher(entry);

search.Filter ="(SAMAccountName="+loginName +")";

search.PropertiesToLoad.Add("givenName");

search.PropertiesToLoad.Add("cn");

search.PropertiesToLoad.Add("mail");

// 如果还需要从AD中获取其它的用户信息,请参考ActiveDirectoryDEMOSearchResultresult =search.FindOne();

if( result !=null) {

UserInfoinfo =newUserInfo();

info.GivenName =result.Properties["givenName"][0].ToString();

info.FullName =result.Properties["cn"][0].ToString();

info.Email =result.Properties["mail"][0].ToString();

returninfo;

}

}

catch{

// 如果需要记录异常,请在此处添加代码。}

return null;

}

private static stringGetDomainName()

{

// 注意:这段代码需要在Windows XP及较新版本的操作系统中才能正常运行。SelectQueryquery =newSelectQuery("Win32_ComputerSystem");

using( ManagementObjectSearchersearcher =newManagementObjectSearcher(query) ) {

foreach( ManagementObjectmo insearcher.Get() ) {

if( (bool)mo["partofdomain"] )

returnmo["domain"].ToString();

}

}

return null;

}

}

使用UserHelper的页面代码:

WindowsAuthentication DEMO - http://www.cnblogs.com/fish-li/
用户短名:
用户全名:
邮箱地址:

当前用户还未登录。

ad 单点登录 java 访问权限_AD 单点登录以及windows认证详细说明相关推荐

  1. ad 单点登录 java 访问权限_如何配置Portal 基于AD的单点登录配置

    Portal for ArcGIS支持两种类型的账户,分别是: 1.系统内置账户. 2.外部系统的企业账户. 这两种不同的账号分别支持多种身份认证方式: 账号类型 认证方式 细分认证方式 系统内置账号 ...

  2. [THINKING IN JAVA]访问权限控制

    6 访问权限控制 6.1 包:库单元 package.import.import *.import static: 修改classpath环境变量可以将自己写的类库添加至环境变量并在任何java程序中 ...

  3. java权限控制是什么_论Java访问权限控制的重要性

    人在什么面前最容易失去抵抗力? 欢迎工作一到八年的Java工程师朋友们加入Java高级交流:854630135 本群提供免费的学习指导 架构资料 以及免费的解答 不懂得问题都可以在本群提出来 之后还会 ...

  4. Java访问权限之 protected详解

    摘要:     对于类的成员(包括成员变量和成员方法)而言,其能否被其他类所访问,取决于该成员的修饰词:而对于一个类而言,其能否被其他类所访问,也取决于该类的修饰词.在Java中,类成员访问权限修饰词 ...

  5. Java访问权限(public、protected、友好的、private)定义

    访问权限定义: 1.访问权限指:对象是否可以通过 " . " 运算符操作自己的变量 或 通过 " . " 运算符调用类中的方法: 2.访问权限修饰符:priva ...

  6. Java访问权限(详尽版)

    Java中的访问权限一共有四种:public .protected . 默认的 .private(访问范围由大到小): 先说public和private这两种,因为这两种最容易理解: public:在 ...

  7. Java访问权限控制

    面向对象的核心思想之一就是封装,只把有限的方法和成员公开给别人,这也是迪米特法则的内在要求,是外部调用方对方法体内的实现细节知道得尽可能少. 如何实现封装呢? 需要使用某些关键字来限制外部对类内属性和 ...

  8. 总结Java访问权限

    Java语言中有四种访问修饰符:friendly.private.public和protected. private:成员变量和方法都只能在定义它的类中被访问,其他类都不能访问: public :能被 ...

  9. java访问权限 public private protected

    作者:yan 1. Java中的访问控制 表1-1 可见/访问性 在同一类中 同一包中 不同包中  同一包子类中  不同包子类中   public  yes  yes  yes  yes  yes   ...

最新文章

  1. LabVIEW做一款科学计算器
  2. 怎么自学python语言-python应用:零基础Python应该怎样学习呢?
  3. [I2C]I2C总线协议图解
  4. postgresql-创建主键自增的表
  5. python的for语句有几种写法_Python if 和 for 的多种写法
  6. 我的世界服务器无限繁殖,我的世界村民无限繁殖方法_我的世界如何无限繁殖村民_牛游戏网...
  7. STL - bitset
  8. ubuntu下面使用stata进行线性回归
  9. 无状态mysql_既然HTTP是无状态协议,mysql_close还有必要么?
  10. maven项目,httpclient jar包冲突
  11. thinkpad重装系统不引导_Thinkpad笔记本重装系统时无法UEFI启动进入PE怎么办
  12. C#中拷贝指定文件夹下的所有文件夹目录到指定文件夹中的方法
  13. 开源计算机集群监控Ganglia应用视频
  14. 实现DropDownList 无刷新的联动效果
  15. Unity3d中使用自带动画系统制作下雨效果(二)
  16. Oracle分页查询与RowNum
  17. RTSP-传送ACC音频文件
  18. 硬盘的老化测试软件,硬盘检测工具使用方法
  19. 哎 !互联网又偷偷进行了裁员
  20. 通过regsrv32.exe绕过Applocker应用程序白名单的多种方法

热门文章

  1. 【渝粤题库】国家开放大学2021春2704植物学基础题目
  2. 控制器局域网can总线
  3. linux alsa 音频管理,在Linux上的高级音频控制
  4. 【6】C++语法与数据结构之STL_list学生管理系统_链表外排序_函数指针
  5. 一文一起,学习功能强大的Java8新StreamAPI,让集合的操作得心应手
  6. php mysql 随机字符串_MySQL_Mysql 自定义随机字符串的实现方法,前几天在开发一个系统,需要 - phpStudy...
  7. 常见的集成逻辑门(CMOS\TTL\ECL)
  8. linux 远程挂载摄像头_如何实现嵌入式Linux下USB摄像头视频采集
  9. vue 圆形百分比进度条_uniapp Vue 圆环进度条
  10. fprintf函数的用法matlab_极力推荐这个Matlab教程