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

时间:2011-12-15 22:35:08

标签: .net active-directory

我正在尝试从活动目录获取更改通知,以便我可以更新数据库中的数据,如果我的AD中有任何变化。我搜索并找到了Ryan Dunn的good example

我尝试实现他的代码。应用程序启动没有任何错误,但它没有生成任何通知。有人可以帮助我吗?

在win 2008服务器计算机上,我的域名为corp.am2k8vm.com,我在活动目录上的用户很少,无法进行测试。

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;}
    }
}

1 个答案:

答案 0 :(得分:4)

即使我对你的应用一无所知,我也会推动你完全考虑一条不同的路径。

变更通知都很好,但有一些缺点。 AD不会扩展到大量的。如果您离线一段时间,则会错过一些更改。等

还有另一种机制我鼓励你考虑命名为DirSync。将DirSync视为AD内部复制协议的“原始公开”,通过LDAP提供给您。 DirSync的想法是你可以发出一个查询并说“改变了什么?”和AD会回答。在答案中是一个不透明的cookie。当您下次再次发出查询时,您再次提供cookie,它将告诉您自上次发布cookie以来发生了哪些更改。

很多很好的元素:

  • DirSync有一个很好的规模故事。您可以要求更改1个对象或100万个,我们知道DirSync将根据您的需求进行扩展。
  • DirSync有一个干净的故事,可以在一段时间内离线。您可以断开连接一两秒钟,然后再回过头来追上您错过的所有内容。
  • DirSync查询非常快。每分钟或类似的事情发一个应该是好的。
  • DirSync有一个干净的多DC故事。您可以在DC之间使用cookie,它(大部分)将适合您。 (我主要是因为你可能会得到重复更改,但就是这样)。
  • 也许最重要的是,DirSync有一个非常干净的一致性故事。我推动使用DirSync的客户在大多数电话中进行有效的DirSync查询,但是时不时(每天?每周?每月?取决于应用......)你可以把cookie扔掉并完全同步。这实际上迫使您真正设计一个干净的e2e解决方案,始终确保您有一个漂亮,安全的方式让您的脱机数据库与AD中的事实保持一致,并且在99%以上的时间内也是有效的。并且“哦出错了”代码路径经过了超级测试,因为它是主线代码路径!它恰好与普通的代码路径相同。

假设您获得重复更改,您需要进行防御性编码,但对于大多数应用来说,这是合理的假设。

希望这有帮助。