通过GUID获取Active Directory对象失败的排查求助
我最近在做Active Directory用户查询的功能时卡壳了:明明能通过SamAccountName成功查到用户并获取到对应的GUID,但反过来用这个GUID去查询同一个用户时,不管试哪种方法都失败——要么返回null,要么直接抛出COM异常。我试了好几种思路,参考了一些示例,但还是没找到问题根源,麻烦帮忙看看我的代码哪里出问题了?
我的完整代码如下:
public class ActiveDirectoryReader { private const string Host = "Redacted"; private const string Username = "Redacted"; private const string Password = "Redacted"; private const string SamName = "Redacted"; [NotNull] [ItemNotNull] private readonly Lazy<string> _lazyLdap; public ActiveDirectoryReader() { _lazyLdap = new Lazy<string>(() => { var ipAddress = Dns.GetHostAddresses(Host)[0]?.ToString() ?? throw new InvalidOperationException(); return $"LDAP://{ipAddress}"; }); } private static string GuidToOctetString(Guid guid) => guid.ToByteArray().Aggregate("", (current, b) => current + @"\\" + b.ToString("x2")); public void Get() { SearchResult searchResult; using (var searcher = new DirectorySearcher( searchRoot: new DirectoryEntry(_lazyLdap.Value, Username, Password), filter: $"(&(objectClass=user)(samAccountName={SamName}))", propertiesToLoad: new[] { "samaccountname", "objectuserGuid" } )) { searchResult = searcher.FindOne(); } var entry = searchResult?.GetDirectoryEntry() ?? throw new DataException(); Guid.TryParseExact(entry.NativeGuid, "N", out var guid); var octetGuid = GuidToOctetString(guid); //Try just as-is using (var searcher = new DirectorySearcher( searchRoot: new DirectoryEntry(_lazyLdap.Value, Username, Password), filter: $"(&(objectClass=user)(objectGUID={guid}))", propertiesToLoad: new[] { "samaccountname", "objectuserGuid" } )) { searchResult = searcher.FindOne();//returns null } //NOTE: I've tried using objectuserGuid instead of objectGuid in the filter. No difference, still returns null. using (var searcher = new DirectorySearcher( searchRoot: new DirectoryEntry(_lazyLdap.Value, Username, Password), filter: $"(&(objectClass=user)(objectGUID={octetGuid}))", propertiesToLoad: new[] { "samaccountname", "objectuserGuid" } )) { searchResult = searcher.FindOne();//returns null } try { using (var searcher = new DirectorySearcher( searchRoot: new DirectoryEntry($"{_lazyLdap.Value}/<GUID={guid}>", Username, Password), filter: $"(&(objectClass=user)(objectGuid={octetGuid}))", propertiesToLoad: new[] { "samaccountname", "objectuserGuid" } )) { searchResult = searcher.FindOne();//returns error } } catch (Exception e) { //Type: System.DirectoryServices.DirectoryServicesCOMException //Message: There is no such object on the server. Console.WriteLine(e); System.Diagnostics.Debugger.Break(); } //Try the last one but with octetGuid try { using (var searcher = new DirectorySearcher( searchRoot: new DirectoryEntry($"{_lazyLdap.Value}/<GUID={octetGuid}>", Username, Password), filter: $"(&(objectClass=user)(objectGuid={octetGuid}))", propertiesToLoad: new[] { "samaccountname", "objectuserGuid" } )) { searchResult = searcher.FindOne();//returns error } } catch (Exception e) { //Type: System.DirectoryServices.DirectoryServicesCOMException //Message: An invalid dn syntax has been specified. Console.WriteLine(e); System.Diagnostics.Debugger.Break(); } return new ActiveDirectoryInfoDto(searchResult?.GetDirectoryEntry(), propertyName, propertyValue); } }
关键问题分析&修正建议
属性名拼写错误(最致命!)
你代码里反复出现的objectuserGuid是错误的AD属性名,正确的用户GUID属性是objectGuid——不管是在propertiesToLoad里指定要加载的属性,还是在查询过滤条件里使用,都要改成objectGuid。这个拼写错误会导致你根本没拿到正确的GUID值,后续所有查询自然全部失效。GUID获取方式可以更简洁可靠
DirectoryEntry本身提供了Guid属性,直接用var guid = entry.Guid;就能拿到正确的Guid对象,完全不需要用NativeGuid再去做TryParseExact,既简洁又不容易出错。OctetString查询的正确姿势
当用objectGuid作为过滤条件时,必须将Guid转成OctetString格式(也就是\XX\XX\XX...的十六进制形式),这部分你的GuidToOctetString方法是对的,但要确保过滤条件里的属性是objectGuid而非错误的拼写。LDAP路径
<GUID=...>的正确用法
用<GUID=...>直接绑定AD对象时,后面跟的应该是标准Guid字符串(比如带连字符的D格式,或不带连字符的N格式),而不是OctetString。比如可以写成:
new DirectoryEntry($"{_lazyLdap.Value}/<GUID={guid.ToString("D")}>", Username, Password)
这种方式可以直接绑定到目标对象,甚至不需要额外加过滤条件。
修正后的核心代码片段
// 第一步:修正属性名,获取正确的GUID using (var searcher = new DirectorySearcher( searchRoot: new DirectoryEntry(_lazyLdap.Value, Username, Password), filter: $"(&(objectClass=user)(samAccountName={SamName}))", propertiesToLoad: new[] { "samaccountname", "objectGuid" } // 修正属性名 )) { searchResult = searcher.FindOne(); } var entry = searchResult?.GetDirectoryEntry() ?? throw new DataException(); var guid = entry.Guid; // 直接用可靠的Guid属性 var octetGuid = GuidToOctetString(guid); // 第二步:用OctetString格式的objectGuid查询 using (var searcher = new DirectorySearcher( searchRoot: new DirectoryEntry(_lazyLdap.Value, Username, Password), filter: $"(&(objectClass=user)(objectGuid={octetGuid}))", // 修正属性名 propertiesToLoad: new[] { "samaccountname", "objectGuid" } )) { searchResult = searcher.FindOne(); // 现在应该能查到结果了 } // 或者直接用GUID绑定DirectoryEntry,跳过搜索步骤 var directEntry = new DirectoryEntry($"{_lazyLdap.Value}/<GUID={guid.ToString()}>", Username, Password); // 直接使用directEntry操作目标对象即可
内容来源于stack exchange

