在.NET中实现Active Directory更改通知

3
我正在尝试从Active Directory获取更改通知,以便在我的AD中有任何更改时更新数据库中的数据。我搜索并找到了Ryan Dunn的良好示例
我尝试实现他的代码。应用程序没有出现任何错误,但没有生成任何通知。有人能帮帮我吗?
我的域是corp.am2k8vm.com,在win 2008服务器上运行,并且我在活动目录中有一些用户进行测试。
using System;
using System.Collections.Generic;
using System.DirectoryServices.Protocols;
using System.DirectoryServices;
namespace ChangeNotifications
{
    class Program
    {
        static void Main(string[] args)
        {
            using (LdapConnection connect = CreateConnection("192.168.182.209"))                //can also use localhost
            {
                using (ChangeNotifier notifier = new ChangeNotifier(connect))
                {
                    //register some objects for notifications (limit 5)
                    notifier.Register("dc=am2k8vm,dc=com", SearchScope.OneLevel);                     //not sure if the parameters are correct here as i am new to active directory stuff
                    notifier.Register("cn=Andy Main,ou=users,dc=am2k8vm,dc=com", SearchScope.Base); //not sure if the parameters are correct here as i am new to active directory stuff
                    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();
        }
        static private LdapConnection CreateConnection(string server)
        {
            LdapConnection connect = new LdapConnection(server);
            connect.SessionOptions.ProtocolVersion = 3;
            connect.AuthType = AuthType.Negotiate;  //use my current credentials
            return connect;
        }
    }
    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;}
    }
}

首先要考虑的是安全性。您是否具备获取通知所需的适当安全性。接下来要考虑的是在AD中进行日志记录 - 也可以在serverfault上询问相关问题。我对AD中的这两个方面一无所知,但这是我开始的方式。 - Preet Sangha
是的,我有管理员权限。 - user1100889
1个回答

4
我会推荐你考虑另一种完全不同的方法,尽管我对你的应用一无所知。
更改通知功能很好,但也有一些缺点。如果更改通知过多,AD无法承载。如果您离线一段时间,您将错过某些更改等等。
我建议您考虑另一种机制,名为DirSync。把DirSync想象成AD内部复制协议的“原始公开”,通过LDAP提供给您。 DirSync的思想是,您可以发出查询并说“什么已经改变了?”然后AD会回答。随着答案而来的是一个不透明的cookie。下次再发出查询时,您再次提供cookie,它就会告诉您自上次发出cookie以来发生了哪些更改。
这有很多好处:
  • DirSync有一个很好的规模故事。您可以要求更改1个对象或100万个对象,我们知道DirSync会满足您的需求。
  • DirSync有一个清晰的离线故事。您可以断开连接1秒钟或1周,然后回来并赶上您错过的所有内容。
  • DirSync查询非常快。每分钟发出一个这样的请求应该没问题。
  • DirSync有一个干净的多-DC故事。您可以在DC之间使用cookie,它会(大多数情况下)正常工作。
  • 也许最重要的是,DirSync有非常干净的一致性故事。我推荐使用DirSync的客户在大多数调用中执行高效的DirSync查询,但每隔一段时间(每天?每周?每月?这取决于应用程序...)您可以简单地丢弃cookie并进行完全同步。这实质上迫使您真正设计一个清晰的端到端解决方案,始终确保您拥有与AD中真实情况对齐且99%+时间内有效的离线数据库的良好且安全的方法。而且“哦,出了些问题”的代码路径经过了很好地测试,因为这是一个主要的代码路径!而且它恰好与正常的代码路径相同。
您需要进行防御性编码,假设您会得到重复的更改,但这对大多数应用程序来说是合理的假设。
希望这能有所帮助。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接