【问题标题】:Implementing Active Directory Change Notifications in .NET在 .NET 中实现 Active Directory 更改通知
【发布时间】:2011-12-15 22:35:07
【问题描述】:

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

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

我的域是 corp.am2k8vm.com 在 win 2008 服务器机器上,我在 Active Directory 上几乎没有用户用于测试目的。

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 中的任何一个都一无所知,但这就是我要开始的方式。
  • 是的,我有管理员权限。

标签: .net active-directory


【解决方案1】:

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

更改通知都很好,但也有一些缺点。 AD 无法扩展到它们的大量数量。如果您离线一段时间,您会错过一些更改。等等。

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

很多不错的元素:

  • DirSync 有一个很好的规模故事。您可以要求对 1 个或 100 万个对象进行更改,我们知道 DirSync 会根据您的需求进行扩展。
  • DirSync 有一个干净的故事,即离线一段时间。您可能会断开连接一秒钟或一周,然后回来补上您错过的所有内容。
  • DirSync 查询速度非常快。每分钟发行一个或类似的东西应该没问题。
  • DirSync 有一个干净的多 DC 故事。您可以跨 DC 使用 cookie,它会(大部分)为您工作。 (我说主要是因为您可能会得到 dup 更改,但仅此而已)。
  • 也许最重要的是,DirSync 有一个非常干净的一致性故事。我推动使用 DirSync 的客户在大多数呼叫中执行高效的 DirSync 查询,但时不时地(每天?每周?每月?取决于应用程序......)你可以扔掉 cookie 并完全同步。这本质上迫使您真正设计一个干净的 e2e 解决方案,始终确保您有一种良好、安全的方式来让您的离线数据库与 AD 中的事实保持一致,并且在 99% 以上的时间里保持高效。并且“哦,出了点问题”的代码路径经过了非常好的测试,因为它是一条主线代码路径!而且恰好和正常的代码路径一样。

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

希望这会有所帮助。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-09-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-11-30
    • 1970-01-01
    相关资源
    最近更新 更多