在 .NET 中获取 AD 上帐号密码过期时间
原文:http://www.blogcn.com/User8/flier_lu/blog/4371854.html
.NET Framework 提供了 System.DirectoryServices 名字空间用于操作 AD 等支持 LDAP 接口的服务器,通过这组类我们能够很容易实现通过 AD 验证用户帐号,以及向 AD 查询域用户及其所在组的信息,是在 Web 应用中集成 AD 以实现企业单点登陆的重要手段之一。
纯朴的狗熊在其 blog 上有一系列非常出色的文章介绍了这方面的基本知识
活动目录.NET编程Tips
使用System.DirectoryServices.Protocols实现对AD的简单操作
ADHelper 活动目录用户操作类
虽然他给出的那个例子代码并不完整,但为后来者提供了很好的基础。
为了让笔者所在公司的基于 SharePoint 的内网门户能够提供一些方便的小功能,如查询自己帐号的密码过期时间等等,笔者对其封装代码做了一些修改,定义了 AdServer/AdGroup/AdUser 分别用于对 AD 服务器/组/用户的封装,让关系更加清晰。回头等封装代码稳定了,再写篇文章详细介绍。
其中碰到一个讨厌的问题是如何从 AD 获取当前帐号的密码过期时间。对基于域的用户来说,通过 ADSI 接口的 WINNT:// 协议,可以简单的从 IADsUser::PasswordExpirationDate 获得这一信息;但对于 AD 的 LDAP:// 协议接口,这个字段并不存在,需要我们手工从帐号最后登陆时间 (pwdLastSet) 和用户所在域的帐号过期时间 (maxPwdAge) 自行计算。
ADSI 接口的 User 对象 schema 中定义了这些常用的属性
User Object Properties
微软 MSDN 中也专门有一篇文章详细介绍了如何进行这种计算
How Long Until My Password Expires?
其核心算法步骤如下:
1.帐号是否被禁用
2.帐号密码是否被设置过
3.帐号所在域是否有密码期限设置
4.计算密码期限设置的天数
5.计算密码过期的时间
算法流程图如下:
对 VBScript 来说只需要一段简单的代码就可以完成任务
但因为 .NET v1.x 中活动目录的简陋封装,使得在 .NET 中要实现上述功能相对较为繁琐。
首先需要通过 AdUser 对象封装的 DirectoryEntry 的属性获得 userAccountControl 字段的值,并判断是否设置了密码永不过期的标志:
然后需要访问密码最后被重置的时间,判断此帐号是否被使用过。
这里需要注意的是,密码最后重置时间 (pwdLastSet) 和域密码过期时间 (maxPwdAge) 等字段在 AD 中是 INTEGER8 类型。虽然理论上对应于 C# 中的 long,但通过 System.DirectoryServices 并不能直接访问之。也就是说对于这些 INTEGER8 类型的字段,通过 Convert.ToInt64(Properties["pwdLastSet"][0]) 这样的强制转换调用会直接抛出异常。
要访问这种字段,必须显式通过 ADSI 规范中的 IADsLargeInteger 接口,手工进行转换。.NET 247 上的一篇文章里面介绍了这个问题的解决方法
DirectoryEntry __ComObject use.
而这个例子中的转换代码还可能出现溢出问题,需要小心处理
Problem with the HighPart and LowPart Property Methods
完整的转换代码如下:
只有解决了这诸多问题,才能将上面那一小段 VBScript 代码真正移植到 .NET 下:
虽然是个小问题,可里面的阻力一点都不小。 希望如 纯朴的狗熊 所说微软会在下个版本里面真正认真对待目录服务这块企业级应用必备的领域。
.NET Framework 提供了 System.DirectoryServices 名字空间用于操作 AD 等支持 LDAP 接口的服务器,通过这组类我们能够很容易实现通过 AD 验证用户帐号,以及向 AD 查询域用户及其所在组的信息,是在 Web 应用中集成 AD 以实现企业单点登陆的重要手段之一。
纯朴的狗熊在其 blog 上有一系列非常出色的文章介绍了这方面的基本知识
活动目录.NET编程Tips
使用System.DirectoryServices.Protocols实现对AD的简单操作
ADHelper 活动目录用户操作类
虽然他给出的那个例子代码并不完整,但为后来者提供了很好的基础。
为了让笔者所在公司的基于 SharePoint 的内网门户能够提供一些方便的小功能,如查询自己帐号的密码过期时间等等,笔者对其封装代码做了一些修改,定义了 AdServer/AdGroup/AdUser 分别用于对 AD 服务器/组/用户的封装,让关系更加清晰。回头等封装代码稳定了,再写篇文章详细介绍。
其中碰到一个讨厌的问题是如何从 AD 获取当前帐号的密码过期时间。对基于域的用户来说,通过 ADSI 接口的 WINNT:// 协议,可以简单的从 IADsUser::PasswordExpirationDate 获得这一信息;但对于 AD 的 LDAP:// 协议接口,这个字段并不存在,需要我们手工从帐号最后登陆时间 (pwdLastSet) 和用户所在域的帐号过期时间 (maxPwdAge) 自行计算。
ADSI 接口的 User 对象 schema 中定义了这些常用的属性
User Object Properties
微软 MSDN 中也专门有一篇文章详细介绍了如何进行这种计算
How Long Until My Password Expires?
其核心算法步骤如下:
1.帐号是否被禁用
2.帐号密码是否被设置过
3.帐号所在域是否有密码期限设置
4.计算密码期限设置的天数
5.计算密码过期的时间
算法流程图如下:
对 VBScript 来说只需要一段简单的代码就可以完成任务
|
首先需要通过 AdUser 对象封装的 DirectoryEntry 的属性获得 userAccountControl 字段的值,并判断是否设置了密码永不过期的标志:
|
然后需要访问密码最后被重置的时间,判断此帐号是否被使用过。
这里需要注意的是,密码最后重置时间 (pwdLastSet) 和域密码过期时间 (maxPwdAge) 等字段在 AD 中是 INTEGER8 类型。虽然理论上对应于 C# 中的 long,但通过 System.DirectoryServices 并不能直接访问之。也就是说对于这些 INTEGER8 类型的字段,通过 Convert.ToInt64(Properties["pwdLastSet"][0]) 这样的强制转换调用会直接抛出异常。
要访问这种字段,必须显式通过 ADSI 规范中的 IADsLargeInteger 接口,手工进行转换。.NET 247 上的一篇文章里面介绍了这个问题的解决方法
DirectoryEntry __ComObject use.
而这个例子中的转换代码还可能出现溢出问题,需要小心处理
Problem with the HighPart and LowPart Property Methods
完整的转换代码如下:
|
只有解决了这诸多问题,才能将上面那一小段 VBScript 代码真正移植到 .NET 下:
|
虽然是个小问题,可里面的阻力一点都不小。 希望如 纯朴的狗熊 所说微软会在下个版本里面真正认真对待目录服务这块企业级应用必备的领域。