如何使用 Novell.Directory.Ldap.NETStandard 和简单分页结果控件在 Ldap 服务器上进行分页搜索?
How to do a paged search on an Ldap server using Novell.Directory.Ldap.NETStandard and Simple Paged Results control?
我正在尝试使用 Novell.Directory.Ldap.NETStandard (https://github.com/dsbenghe/Novell.Directory.Ldap.NETStandard) and Simple Paged Results control (https://ldapwiki.com/wiki/Simple%20Paged%20Results%20Control) 在 Active Directory 中进行分页搜索。
第一页工作正常,但第二页在 searchResult.next() 行抛出 "Unavailable Critical Extension"。在查看 ActiveDirectory 的事件日志时,我发现:
00000057:LdapErr:DSID-0C090809,注释:错误处理控制,数据 0,v23f0
0000208D:NameErr:DSID-03100213,问题 2001 (NO_OBJECT),数据 0,最佳匹配:
我们也尝试了 LdapVirtualListControl 但 运行 遇到了不同的问题,请参阅 How to do a paged search on an Ldap server with > 10000 entries using Novell.Directory.Ldap.NETStandard?
这是我们用来重现的简化代码:
// Connection
var ldapConn = new LdapConnection()
{
SecureSocketLayer = true,
};
ldapConn.UserDefinedServerCertValidationDelegate += (sender, certificate, chain, sslPolicyErrors) => true;
ldapConn.Connect(host, 636);
ldapConn.Bind(username, password);
// Constraints
LdapSearchConstraints searchConstraints = (LdapSearchConstraints)_conn.SearchConstraints.Clone();
int pageSize = 100, count = 0;
bool exit = false;
const string LDAP_SERVER_SIMPLE_PAGED_RESULT_OID = "1.2.840.113556.1.4.319";
LdapControl pageControl = null;
do
{
int inPageCount = 0;
// Add Simple Paged Result control
var request = new Asn1Sequence(2);
request.add(new Asn1Integer(pageSize));
request.add(pageControl == null ? new Asn1OctetString("") : new Asn1OctetString(pageControl.getValue()));
searchConstraints.setControls(
new LdapControl(LDAP_SERVER_SIMPLE_PAGED_RESULT_OID, true, request.getEncoding(new LBEREncoder()))
);
// Get search result
var searchResult = (LdapSearchResults)ldapConn.Search(container, LdapConnection.SCOPE_SUB, query, null, false, searchConstraints);
while (searchResult.hasMore())
{
// Detect simple paged result control
pageControl = searchResult.ResponseControls?.Where(rc => rc.ID == LDAP_SERVER_SIMPLE_PAGED_RESULT_OID).FirstOrDefault();
if (pageControl != null) break;
var nextEntry = searchResult.next();
inPageCount++;
}
count += inPageCount;
// Exit if no more pages
exit = pageControl == null;
} while (!exit);
为什么代码不起作用
根据RFC简单分页结果控件编码为
realSearchControlValue ::= SEQUENCE {
size INTEGER (0..maxInt),
-- requested page size from client
-- result set size estimate from server
cookie OCTET STRING
}
在下一个屏幕截图(取自 Wireshark)中可能会很清楚。
:
当客户端向请求添加控件时,size 设置为页面中所需的元素数,cookie 是来自先前服务器响应的不透明结构(第一个请求为空)。
当您尝试在请求中构建控件时,您错误地添加了整个控件值而不是 cookie (pageControl.getValue()):
var request = new Asn1Sequence(2);
request.add(new Asn1Integer(pageSize));
request.add(pageControl == null ? new Asn1OctetString("") : new Asn1OctetString(pageControl.getValue()));
它使第一个请求之后的所有请求都不正确。
建议的解决方案
看看https://github.com/metacube/PagedResultsControl。
我创建了类型化的简单分页结果控件实现,它封装了解码/编码逻辑。在来自 Active Directory 的 100 000 多个条目的情况下,对我来说工作得很好。
测试应用程序显示了基本用法。
我正在尝试使用 Novell.Directory.Ldap.NETStandard (https://github.com/dsbenghe/Novell.Directory.Ldap.NETStandard) and Simple Paged Results control (https://ldapwiki.com/wiki/Simple%20Paged%20Results%20Control) 在 Active Directory 中进行分页搜索。
第一页工作正常,但第二页在 searchResult.next() 行抛出 "Unavailable Critical Extension"。在查看 ActiveDirectory 的事件日志时,我发现:
00000057:LdapErr:DSID-0C090809,注释:错误处理控制,数据 0,v23f0 0000208D:NameErr:DSID-03100213,问题 2001 (NO_OBJECT),数据 0,最佳匹配:
我们也尝试了 LdapVirtualListControl 但 运行 遇到了不同的问题,请参阅 How to do a paged search on an Ldap server with > 10000 entries using Novell.Directory.Ldap.NETStandard?
这是我们用来重现的简化代码:
// Connection
var ldapConn = new LdapConnection()
{
SecureSocketLayer = true,
};
ldapConn.UserDefinedServerCertValidationDelegate += (sender, certificate, chain, sslPolicyErrors) => true;
ldapConn.Connect(host, 636);
ldapConn.Bind(username, password);
// Constraints
LdapSearchConstraints searchConstraints = (LdapSearchConstraints)_conn.SearchConstraints.Clone();
int pageSize = 100, count = 0;
bool exit = false;
const string LDAP_SERVER_SIMPLE_PAGED_RESULT_OID = "1.2.840.113556.1.4.319";
LdapControl pageControl = null;
do
{
int inPageCount = 0;
// Add Simple Paged Result control
var request = new Asn1Sequence(2);
request.add(new Asn1Integer(pageSize));
request.add(pageControl == null ? new Asn1OctetString("") : new Asn1OctetString(pageControl.getValue()));
searchConstraints.setControls(
new LdapControl(LDAP_SERVER_SIMPLE_PAGED_RESULT_OID, true, request.getEncoding(new LBEREncoder()))
);
// Get search result
var searchResult = (LdapSearchResults)ldapConn.Search(container, LdapConnection.SCOPE_SUB, query, null, false, searchConstraints);
while (searchResult.hasMore())
{
// Detect simple paged result control
pageControl = searchResult.ResponseControls?.Where(rc => rc.ID == LDAP_SERVER_SIMPLE_PAGED_RESULT_OID).FirstOrDefault();
if (pageControl != null) break;
var nextEntry = searchResult.next();
inPageCount++;
}
count += inPageCount;
// Exit if no more pages
exit = pageControl == null;
} while (!exit);
为什么代码不起作用
根据RFC简单分页结果控件编码为
realSearchControlValue ::= SEQUENCE {
size INTEGER (0..maxInt),
-- requested page size from client
-- result set size estimate from server
cookie OCTET STRING
}
在下一个屏幕截图(取自 Wireshark)中可能会很清楚。
当客户端向请求添加控件时,size 设置为页面中所需的元素数,cookie 是来自先前服务器响应的不透明结构(第一个请求为空)。
当您尝试在请求中构建控件时,您错误地添加了整个控件值而不是 cookie (pageControl.getValue()):
var request = new Asn1Sequence(2);
request.add(new Asn1Integer(pageSize));
request.add(pageControl == null ? new Asn1OctetString("") : new Asn1OctetString(pageControl.getValue()));
它使第一个请求之后的所有请求都不正确。
建议的解决方案
看看https://github.com/metacube/PagedResultsControl。 我创建了类型化的简单分页结果控件实现,它封装了解码/编码逻辑。在来自 Active Directory 的 100 000 多个条目的情况下,对我来说工作得很好。
测试应用程序显示了基本用法。