• 一个Exchange后台管理程序(WEB)其一 用户检索


     

    引言

    如果一个Exchange的用户群按照部门的形式分散在各地,而人数又很多,让一个管理员管理工作量实在有点大。一个方式是编写一个WEB程序发布到IIS,然后让各个分部门的管理员管理各自的部门。没错,本系列文章就是围绕这个话题展开的。而这个话题的核心是如何通过.NET管理Exchange。嗯,似乎是这样。当时在实现系统的过程中,我碰到了很多其他的问题,也非常有趣。所以我决定,按照实现的流程来记叙,列出碰到的一些问题和解决方式。解决问题的过程中参考了很多网友的博客,同时得到了很多网友的帮助,会在适当的地方给出引用,方便大家参考。

    这个系统应该包括以下功能:查询,查看(指定的一些属性),禁用/启用,解锁,删除,新建,编辑(指定的一些属性),统计各个部门人数,为分部门指派管理员。总的来说就是增删查改。

    用户查询

    Exchange的用户信息是和AD同步的,符合微软企业级产品的风格。所以查询用户信息可以通过查询AD进行。在.NET下可以通过System.DirectoryServices名称空间(处于System.DirectoryServices程序集中)进行活动目录查询。使用到类型是DirectorySearcher。当然System.DirectoryService.AccountManagement名称空间中的PrincipalSearcher也可以进行用户查询,但是在执行一些操作的时候,还是前者方便。所以,这里使用前一个方案。

    查询功能设计成按照用户的UPN(登录名)和姓名(环境中使用CN以方便显示)搜索,在列表中显示以下信息:登录名,CN,显示名称,所属部门,是否禁用。

    分页

    要进行分页就需要获得搜索结果的总条数,我一开始没多在意DirectorySearcehr的属性,直接指定了Filter属性和根结点,然后指定搜索范围为SubTree,结果当搜索条数为1200条的时候,统计个数就要十来秒。而且我还很2X的调用GetDirectoryEntry来访问其属性,结果查询超慢。经过朋友的指点,参考了这篇博客:http://blog.sina.com.cn/s/blog_683424be0100scuz.html,才有效的缩短了查询时间。

    主要是PropertiesToLoad属性,通过向这个集合添加键,可以指定加载哪些DirectoryEntry属性,剩余属性不加载,从而缩短了时间。注意,Filter中涉及的属性是需要加载的。以下是一段脚本,和结果,可以看到差别之大。

    可用的属性键有:

    msexchumenabledflags2,homemdb,legacyexchangedn,usncreated,msexchhomeservername,m
    sexchrecipientdisplaytype,msexchrbacpolicylink,samaccountname,showinaddressbook,
    msexchmailboxauditenable,cn,pwdlastset,whencreated,displayname,lastlogon,garbage
    collperiod,samaccounttype,countrycode,objectguid,msexchmailboxsecuritydescriptor
    ,msexchmdbrulesquota,usnchanged,msexcharchivewarnquota,msexchversion,whenchanged
    ,msexchmoderationflags,name,msexchwhenmailboxcreated,protocolsettings,msexchpoli
    ciesincluded,objectsid,logoncount,internetencoding,mailnickname,msexchuseraccoun
    tcontrol,msexchtransportrecipientsettingsflags,msexchprovisioningflags,badpasswo
    rdtime,accountexpires,msexchaddressbookflags,primarygroupid,objectcategory,userp
    rincipalname,proxyaddresses,msexchdumpsterquota,useraccountcontrol,dscorepropaga
    tiondata,mdbusedefaults,distinguishedname,msexchmailboxauditlogagelimit,objectcl
    ass,badpwdcount,msexchrecipienttypedetails,homemta,mail,adspath,msexcharchivequo
    ta,msexchumdtmfmap,msexchmailboxguid,lastlogoff,msexchbypassaudit,instancetype,c
    odepage,msexchdumpsterwarningquota,
    

     在我的例子中,使用cn,displayname,mail,samaccountname(和UPN同名),distinguishedname(通过解析获取OU信息),useraccountcontrol。

    这里有一点需要注意,上面截图中,没有指定PageSize,所以就算指定SizeLimit,都只会最多返回1000条。

    内存分页?LINQ迭代?

    对1000条进行测试,速度上,两个区别不是很大。我最开始是用内存分页的,就是转换了(select)之后转为数组(ToArray),后来改为Skip,和Take的查询。在代码中进行测试的时候,设置好PageSize属性,然后使用foreach迭代输出,可以发现输出结果是一段一段的,输出若干个(页长)停顿一小会儿。所以使用Skip和Take方式最好,网上已经有LINQ to AD查询的实现了(LINQ Provider)。

    用户操作

    操作用户信息的时候有几个小点,这里直接列出。

    #更改CN。使用DirectoryEntry修改对象的CN的属性的时候应该使用以下方式。

    DirectoryEntry.Rename("CN=" + cmname),需要注意参数的格式。

    #如何判断用户是否禁用。

    1.通过DirecotryEntry.Properties["UserAccountControl"]的值和2进行位与(&)操作,然后转为bool,含义是Disabled(512&2的结果为0代表正常)

    2.通过获取UserPrincipal,然后访问其Enabled属性。

    #判断用户是否锁定。

    DirectoryEntry.InvokeGet(“IsAccountLocked”)

    #重置密码。重置密码后如何把用户置为“下次登录必须修改密码”。

    1.通过DirectoryEntry获取UserPrincipal对象,调用ExpirePasswordNow()方法。

    2.通过DirectoryEntry自身来处理应该也可行,但是尚未尝试。

    #修改属性的正确的方式(防止异常)。这里把键和值对应了一下,不是必要的。

             foreach (var kv in propertyValues)
                {
                    if (!string.IsNullOrEmpty(kv.Value))
                        if (entry.Properties.Contains(kv.Key))
                            entry.Properties[kv.Key][0] = kv.Value;
                        else
                            entry.Properties[kv.Key].Add(kv.Value);
                }
                entry.CommitChanges();    
    

     分页窗口

    由于以前学过一点C(都不好意思说出口),考虑问题的时候总是面向过程。就算知道建立类型,一些思考方向还是没有转过来。这里表现的就很明显。面对的问题是:让分页栏保持定长(如果有足够多页的数据)。

    #CStyle

    获取当前页,获取窗口长度。计算左右距离,验证左侧,验证右侧。然后在不同的情况下取不同的值。比如:

    protected int[] getPagerWindow(int current, int max, int width)
            {
                //获取分页窗口
                int left = (width - 1) / 2;
                int right = width - left - 1;
                //左右索引
                int rindex;
                int lindex;
                List<int> window = new List<int>();
                //计算
                if (current - left <= 0)
                {
                    //获取分页窗口左右索引
                    lindex = 1;
                    rindex = Math.Min(width, max);
                }
                else if (current + right >= max)
                {
                    //获取左右索引
                    rindex = max;
                    lindex = Math.Max(1, max - width + 1);
                }
                else
                {
                    rindex = current + right;
                    lindex = current - left;
                }
                for (int i = lindex; i < rindex + 1; i++)
                    window.Add(i);
    
                return window.ToArray();
            }
    

    #OOPStyle

    将窗口想象成窗口,将页码想象成长胶卷。那么,如果胶卷长度比窗口小,就取所有页码,否则计算窗口中心的位移,然后移动窗口。比如:

            public static IEnumerable<int> ComputePageWindow(int count, int current, int size)
            {
                current = current < 1 ? 1 : current;
                count = count < 1 ? 1 : count;
    
                var fixedIndex = size%2 == 0 ? size/2 : (size + 1)/2;
                var offset = current - fixedIndex;
                offset = current < fixedIndex ? 0 : offset;
                offset = count - current < fixedIndex ? count - size : offset;
                var windowStart = 1 + offset;
                return Enumerable.Range(windowStart, size).Where(i => i > 0 && i <= count);
            }

    #以及数据绑定

    <asp:Repeater ID="rptPager" runat="server" 
    DataSource='<%#getPagerWindow(searcher.CurrentIndex,searcher.PageCount,5) %>'
    OnItemCommand="rptPager_ItemCommand">
    </asp:Repeater>

    很明显,第二种思路简洁很多。这里记下来提醒自己。各位朋友就当是听一个初学者的喃喃自语吧。

    自此,AD信息管理的内容结束,其实主要是为了解决搜索的问题。这里贴一个运行截图。由于操作中有数据转换,所以执行搜索花了2~3秒,但是作为一个管理系统还可以接受。下一篇的内容是.net调用PowerShell组件以达到管理Exchange的目的。

  • 相关阅读:
    前端(移动端)开发沉思录
    我的世界观—学习与世界观
    学习一门新的语言和平台相当于学习一个新的世界观
    股票:格局、模式、结构与策略
    资本市场世界观
    投资的道与术:投资投的是自己的修为
    关心把事情做得正确,而不是关心赚钱--《股票作手回忆录》第十一章--投资股票的道与术
    js字符串截取函数slice()、substring()、substr()
    推荐一个好的Redis GUI 客户端工具
    sometimes-ever js中创建数组,并往数组里添加元素
  • 原文地址:https://www.cnblogs.com/lightluomeng/p/2867019.html
Copyright © 2020-2023  润新知