C#通过LDAP访问目录服务
C#通过LDAP访问目录服务
本文介绍如何编写C#程序通过LDAP协议访问微软目录服务获得用户在目录中的属性信息。在开始部分先简单句介绍LDAP协议,然后是技术比较及实现部分。
目录
- 什么是LDAP?
- 简介
- 概述
- 协议内容
- 产品
- 查询用户信息
- 用途
什么是LDAP?
简介
LDAP是轻型目录访问协议(Lightweight Directory Access Protocol,/ˈɛldæp/),是一个开放的、中立的、工业标准的应用协议,通过IP协议提供访问控制和维护分布式信息的目录信息访问服务。
目录服务在开发内部网路和与互联网应用共享用户(用户组织)、系统、网络、服务和应用的过程中占据了重要地位。例如,目录服务提供了组织有序的记录集合,通常有层级结构,例如公司电子邮件目录。同理,也提供包含了地址和电话号码的电话簿。
LDAP由互联网工程任务组(IETF)的文档RFC定义,使用了描述语言ASN.1定义。最新的版本是版本3,由RFC 4511所定义。例如,一个用语言描述的LDAP的搜索如:“在公司邮件目录中搜索公司位于那什维尔名字中含有“Jessy”的有邮件地址的所有人。请返回他们的全名,电子邮件,头衔和简述。”
LDAP的一个常用用途是单点登录,用户可以在多个服务中使用同一个密码,通常用于公司内部网站的登录中(这样他们可以在公司计算机上登录一次,便可以自动在公司内部网上登录)。
LDAP基于X.500标准的子集。因为这个关系,LDAP有时被称为X.500-lite。
概述
鉴于原先的目录访问协议(Directory Access Protocol即DAP)对于简单的互联网客户端使用太复杂,IETF设计并指定LDAP做为使用X.500目录的更好的途径。LDAP在TCP/IP之上定义了一个相对简单的升级和搜索目录的协议。
常用词"LDAP目录"可能会被误解,而实际并没有"LDAP目录"这么一个目录种类。通常可以用它来描述任何使用LDAP协议访问并能用X.500标识符标识目录中对象的目录。
协议内容
LDAP目录与普通数据库结构的主要不同之处在于数据的组织方式,它是一种有层次的、树形结构。所有条目的属性的定义是对象类(object class)的组成部分,并组成在一起构成结构(schema);那些在组织内代表个人的结构被命名为白页结构(white pages schema)。数据库内的每个条目都与若干对象类联系,而这些对象类决定了一个属性是否为可选和它保存哪些类型的信息。属性的名字一般是一个易于记忆的字符串,例如用cn为通用名(common name)命名,而"mail"代表e-mail地址。属性取值依赖于其类型,并且LDAPv3中一般非二进制值都遵从UTF-8字符串语法。例如,mail属性包含值“user@example.com”;jpegPhotos属性一般包含JPEG/JFIF格式的图片。
LDAP目录条目可描述一个层次结构,这个结构可以反映一个政治、地理或者组织的范畴。在原始的X.500模型中,反应国家的条目位于树的顶端;接着是州或者民族组织。典型的LDAP配置使用DNS名称作为树形结构的顶端,下列是代表人、文档、组织单元、打印机和其他任何事务的条目。
LDAP影响了后续的Internet协议,包括新版本的X.500、Directory Services Markup Language (DSML)、Service Provisioning Markup Language (SPML)和Service Location Protocol.
产品
LDAP从下面厂商获得广泛支持:
- Apache - Apache Directory Server
- Apple(苹果) - Open Directory
- AT&T
- Banyan
- HP(惠普)
- IBM - IBM Lotus
- ISODE - M-Vault Server
- Microsoft(微软) - Active Directory
- Netscape(网景) - Sun Microsystems和Red Hat的产品
- Novell - eDirectory
- OctetString - VDE Server
- Oracle(甲骨文) - Oracle Internet Directory
- Red Hat(红帽) - Fedora Directory Server
- Siemens(西门子) - DirX Server
- Sun - iPlanet Directory Server
此外还有开源/自由软件的实现——如Open LDAP、Apache HTTP Server使用代理服务器(通过模块mod_proxy)支持LDAP。
查询用户信息
技术选型
微软Windows活动目录的目录服务还是通过微软的技术框架来实现,无论从安全性和稳定性上都会有一定的保证,所以我们采用C#,当然你也可以使用C++实现,相信会有更好的性能提升。
目录服务程序集
这里我们介绍一下目录服务相关的程序集:
名称 | 说明 |
---|---|
System.DirectoryServices | 利用System.DirectoryServices命名空间,可以方便地从托管代码中访问AD域服务。 该命名空间包含两个组件类,即DirectoryEntry和DirectorySearcher,它们使用AD服务接口 (ADSI) 技术。 ADSI是Microsoft 提供的一组接口,作为使用各种网络提供程序的灵活的工具。 无论网络有多大,ADSI 都可以使管理员能够相对容易地定位和管理网络上的资源。 |
System.DirectoryServices.AccountManagement | 命名空间提供了统一的访问和用户操作、计算机和组安全原则(在多个主要存储):活动目录域服务 (AD DS)、 活动目录轻量级目录服务 (AD LDS)和机器 SAM (MSAM)。 管理独立于 命名空间的目录对象。 |
System.DirectoryServices.ActiveDirectory | 命名空间提供一个围绕Microsoft AD服务任务构建的高级别抽象对象模型。AD服务概念(例如,林、域、站点、子网、分区和架构)是此对象模型的一部分。 |
System.DirectoryServices.Protocols | 命名空间提供在轻量目录访问协议 (LDAP) 3 版 (V3) 和目录服务标记语言 (DSML) 2.0 版 (V2) 标准中定义的方法。 |
两种方法的实现与比较
为了获得用户的目录信息,看来我们需要使用的是前两个程序集和名称空间。
使用System.DirectoryServices.AccountManagement获取用户信息示例:
public DirectoryEntry GetEntryByUsername(string username)
{
PrincipalContext ctx = new PrincipalContext(ContextType.Domain, domain);
UserPrincipal user = UserPrincipal.FindByIdentity(ctx, username);
DirectoryEntry entry = user.GetUnderlyingObject() as DirectoryEntry;
return entry;
}
第一次运行花费时间630ms
还有一种方法是直接使用目录搜索
public SearchResult GetEntryByUsername(string username)
{
DirectorySearcher search = new DirectorySearcher();
search.Filter = $"(&(objectClass=user)(sAMAccountName={username}))";
SearchResult result = search.FindOne();
}
第一次运行花费时间355ms
而且后续处理的事件花费差异也很大,使用DirectoryEntry获得的对象,所有属性还需要按其结构进行遍历:
public static void ShowProperties(DirectoryEntry entry)
{
PropertyCollection props = entry.Properties;
foreach (string k in props.PropertyNames)
{
Console.WriteLine("{0}: {1}", k, props[k].Value);
if (props[k].Value is Array)
{
Array arr = (Array)props[k].Value;
foreach (object obj in arr)
{
Console.WriteLine(" Item: " + obj + " Type: " + obj.GetType().Name);
}
}
}
}
而通过DirectorySearcher查询,不但查询速度快,结果似乎也是直接缓存中读出来的,高效快速。
if (result != null)
{
// user exists, cycle through LDAP fields (cn, telephonenumber etc.)
ResultPropertyCollection fields = result.Properties;
foreach (String ldapField in fields.PropertyNames)
{
// cycle through objects in each field e.g. group membership
// (for many fields there will only be one object such as name)
foreach (Object myCollection in fields[ldapField])
Console.WriteLine(String.Format("{0,-20} : {1}",
ldapField, myCollection.ToString()));
}
}
所以这里推荐使用DirectorySearcher直接进行目录对象(不仅限于用户,还有组织单元、计算机、其它设备等信息)的搜索和详细信息输出,效率会提升不少。
用途
利用Microsoft目录服务管理的公司,可以通过目录属性进行网络、设备等进行基于角色的访问控制,通过不同的AD组或设置目录属性,采用不同的管理策略。而IT经常需要对此过程进行自动化处理,所以通过LDAP进行目录服务数据的更改和访问就必不可少,还可以自动化权限(AD组、属性)的复查流程,减少组织和企业管理的人工成本。