使用C#向Active Directory注册更改通知

Sam*_*Sam 11 c# monitor active-directory notify

此链接http://msdn.microsoft.com/en-us/library/aa772153(VS.85).aspx说:

您最多可以在一个LDAP连接上注册五个通知请求.您必须有一个专用线程,等待通知并快速处理它们.当您调用ldap_search_ext函数来注册通知请求时,该函数会返回标识该请求的消息标识符.然后使用ldap_result函数等待更改通知.发生更改时,服务器会向您发送一条LDAP消息,其中包含生成通知的通知请求的消息标识符.这会导致ldap_result函数返回标识已更改对象的搜索结果.

通过.NET文档我找不到类似的行为.如果有人知道如何在C#中做到这一点,我将非常感谢知道.我希望看到系统中所有用户的属性发生变化,以便我可以根据更改的内容执行自定义操作.

我查看了stackoverflow和其他来源没有运气.

谢谢.

stu*_*rtd 18

我不确定它能满足您的需求,但请查看http://dunnry.com/blog/ImplementingChangeNotificationsInNET.aspx

编辑:添加文章中的文字和代码:



有三种方法可以找出Active Directory(或ADAM)中已更改的内容.这些已在MSDN中记录了一段时间,名为" 变更跟踪技术概述".综上所述:

  1. 使用uSNChanged轮询更改.此技术检查"highestCommittedUSN"值以启动,然后搜索随后更高的"uSNChanged"值."域控制器"之间不会复制"uSNChanged"属性,因此每次都必须返回相同的域控制器以保持一致性.基本上,您执行搜索以查找最高'uSNChanged'值+ 1,然后读取结果以您希望的任何方式跟踪它们.
    • 优点
      • 这是最兼容的方式.所有语言和所有版本的.NET都支持这种方式,因为它是一个简单的搜索.
    • 缺点
      • 这里有很多让开发人员照顾的地方.您将获得整个对象,并且必须确定对象上已更改的内容(如果您关心该更改).
      • 处理已删除的对象是一件痛苦的事.
      • 这是一种轮询技术,因此它只与您查询的频率一样实时.取决于应用程序,这可能是一件好事.注意,这里也没有跟踪中间值.
  2. 使用DirSync控件轮询更改.此技术使用ADSI中的ADS_SEARCHPREF_DIRSYNC选项和封面下的LDAP_SERVER_DIRSYNC_OID控件.只需进行初始搜索,存储cookie,然后再次搜索并发送cookie.它只返回已更改的对象.
    • 优点
      • 这是一个容易遵循的模型.System.DirectoryServices和System.DirectoryServices.Protocols都支持此选项.
      • 过滤可以减少您需要打扰的内容.例如,如果我的初始搜索是针对所有用户"(objectClass = user)",我随后可以使用"(sn = dunn)"过滤轮询,并且只返回两个过滤器的组合,而不是必须处理来自初始过滤器的一切.
      • Windows 2003+选项消除了使用此选项(对象安全性)的管理限制.
      • Windows 2003+选项还使您能够仅返回在大型多值属性中已更改的增量值.这是一个非常好的功能.
      • 与删除的对象相处得很好.
    • 缺点
      • 这是.NET 2.0+或更高版本的选项..NET 1.1的用户需要使用uSNChanged Tracking.脚本语言不能使用此方法.
      • 您只能将搜索范围限定为分区.如果您只想跟踪特定的OU或对象,则必须稍后自己整理这些结果.
      • 在非Windows 2003模式域中使用此选项时,您必须具有要使用的复制获取更改权限(仅限默认管理员)的限制.
      • 这是一种轮询技术.它也不跟踪中间值.因此,如果要跟踪的对象在搜索之间多次更改,则只会获得最后一次更改.根据应用,这可能是一个优势.
  3. 在Active Directory中更改通知.此技术在单独的线程上注册搜索,当任何对象更改与筛选器匹配时,该线程将接收通知.每个异步连接最多可以注册5个通知.
    • 优点
      • 即时通知.其他技术需要轮询.
      • 因为这是一个通知,所以您将获得所有更改,即使是在其他两种技术中也会丢失的中间更改.
    • 缺点
      • 相对资源密集.您不希望执行大量这些操作,因为它可能会导致控制器出现可伸缩性问题.
      • 这只会告诉您对象是否已更改,但它不会告诉您更改的内容.您需要确定您关心的属性是否已更改.话虽如此,很容易判断对象是否已被删除(至少比uSNChanged轮询更容易).
      • 您只能在非托管代码或System.DirectoryServices.Protocols中执行此操作.

在大多数情况下,我发现DirSync在几乎所有情况下都适合我.我从不打扰尝试任何其他技术.但是,读者询问是否有办法在.NET中进行更改通知.我认为可以使用SDS.P,但从未尝试过.事实证明,这可能并且实际上并不太难.

我写这篇文章的第一个想法是使用在MSDN上找到的示例代码(并从选项#3引用)并简单地将其转换为System.DirectoryServices.Protocols.结果证明这是一个死胡同.你在SDS.P中的方式和示例代码的工作方式是不同的,它没有任何帮助.这是我提出的解决方案:

public class ChangeNotifier : IDisposable
{
    LdapConnection _connection;
    HashSet<IAsyncResult> _results = new HashSet<IAsyncResult>();

    public ChangeNotifier(LdapConnection connection)
    {
        _connection = connection;
        _connection.AutoBind = true;
    }

    public void Register(string dn, SearchScope scope)
    {
        SearchRequest request = new SearchRequest(
            dn, //root the search here
            "(objectClass=*)", //very inclusive
            scope, //any scope works
            null //we are interested in all attributes
            );

        //register our search
        request.Controls.Add(new DirectoryNotificationControl());

        //we will send this async and register our callback
        //note how we would like to have partial results

        IAsyncResult result = _connection.BeginSendRequest(
            request,
            TimeSpan.FromDays(1), //set timeout to a day...
            PartialResultProcessing.ReturnPartialResultsAndNotifyCallback,
            Notify,
            request);

        //store the hash for disposal later

        _results.Add(result);
    }

    private void Notify(IAsyncResult result)
    {
        //since our search is long running, we don't want to use EndSendRequest
        PartialResultsCollection prc = _connection.GetPartialResults(result);

        foreach (SearchResultEntry entry in prc)
        {
            OnObjectChanged(new ObjectChangedEventArgs(entry));
        }
    }

    private void OnObjectChanged(ObjectChangedEventArgs args)
    {
        if (ObjectChanged != null)
        {
            ObjectChanged(this, args);
        }
    }

    public event EventHandler<ObjectChangedEventArgs> ObjectChanged;

    #region IDisposable Members

    public void Dispose()
    {
        foreach (var result in _results)
        {
            //end each async search
            _connection.Abort(result);

       }
    }

    #endregion
}


public class ObjectChangedEventArgs : EventArgs
{
    public ObjectChangedEventArgs(SearchResultEntry entry)
    {
        Result = entry;
    }

    public SearchResultEntry Result { get; set;}
}
Run Code Online (Sandbox Code Playgroud)

这是一个相对简单的类,可用于注册搜索.诀窍是在回调方法中使用GetPartialResults方法来仅获取刚刚发生的更改.我还包括了我用来传回结果的非常简化的EventArgs类.注意,我没有做任何关于线程的事情,我没有任何错误处理(这只是一个示例).您可以像这样使用此类:

static void Main(string[] args)
{
    using (LdapConnection connect = CreateConnection("localhost"))
    {
        using (ChangeNotifier notifier = new ChangeNotifier(connect))
        {
            //register some objects for notifications (limit 5)
            notifier.Register("dc=dunnry,dc=net", SearchScope.OneLevel);
            notifier.Register("cn=testuser1,ou=users,dc=dunnry,dc=net", SearchScope.Base);

            notifier.ObjectChanged += new EventHandler<ObjectChangedEventArgs>(notifier_ObjectChanged);

            Console.WriteLine("Waiting for changes...");
            Console.WriteLine();
            Console.ReadLine();
        }
    }
}


static void notifier_ObjectChanged(object sender, ObjectChangedEventArgs e)
{
    Console.WriteLine(e.Result.DistinguishedName);

    foreach (string attrib in e.Result.Attributes.AttributeNames)
    {
        foreach (var item in e.Result.Attributes[attrib].GetValues(typeof(string)))
        {
            Console.WriteLine("\t{0}: {1}", attrib, item);
        }
    }
    Console.WriteLine();
    Console.WriteLine("====================");
    Console.WriteLine();
}
Run Code Online (Sandbox Code Playgroud)

  • @Marco我发布八年了吗?我不知道。你为什么不试试看 (2认同)