最近公司有需求要同时对接AD域和OpenLDAP,把OpenLDAP的组织和员工数据同步到AD域中,于是去网上找了很多资料,大多数都不全或者有问题,后面我一步步摸索出来了自己的经验,现在分享出来。
分享的内容为主流程代码,采用.net core 3.1+Quartz定时服务+Ad域+OpenLDAP+对接钉钉部分功能完成的。其他代码因为可能涉及公司著作权的问题就不贴了,有疑问可以评论文章问我。部分LDAPHelper的内容采用了其他博客的部分代码。注意,虽然下面代码是用.net core开发的,但是只支持部署到windows,因为System.DirectoryServices只支持windows平台,不支持linux平台,若要支持linux,则不能使用System.DirectoryServices及其内部函数,得使用linux专用的操作目录服务器的.net core sdk,具体是哪个sdk大家可以去搜索下。
废话不多说直接上代码:
using Quartz;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using log4net;
using ZT.Log;
using System.DirectoryServices;
using ZT.DataSyncService.HRToContract.Helper;
using ZT.DataSyncService.HRToContract.Entity;
using System.Linq;
using System.Net.Mail;
using System.IO;
using System.Text;
using Newtonsoft.Json;
using DingTalk.Api;
using DingTalk.Api.Request;
using DingTalk.Api.Response;
namespace ZT.DataSyncService.HRToContract.Jobs
{
[DisallowConcurrentExecution]
public class AdLDAPJob : IJob
{
public static List<SendEmailEmployee> newEmployees = new List<SendEmailEmployee>();
private static LdapHelper adHelper = null;
private static LdapHelper openHelper = null;
//预警人数统计
private static int W_AddUserCount = 0;
private static int W_DisUserCount = 0;
//常规人数统计
private static int I_AddUserCount = 0;
private static int I_MoveUserCount = 0;
private static int I_DisUserCount = 0;
private static DateTime I_LastSendTime = DateTime.MinValue;
private ILog log = LogManager.GetLogger(Program.repository.Name, typeof(AdLDAPJob));
public Task Execute(IJobExecutionContext context)
{
try
{
log.Info("AdLDAPJob运行开始");
RunFunc();
log.Info("AdLDAPJob运行结束");
}
catch (Exception ex)
{
log.Error("AdLDAPJob/Execute异常", ex);
}
return Task.CompletedTask;
}
public void RunFunc()
{
var adDomain = AppConfigurtaionServices.Configuration.GetSection("AdLDAP:adDomain").Value;
var adDomain2 = AppConfigurtaionServices.Configuration.GetSection("AdLDAP:adDomain2").Value;
var adIP = AppConfigurtaionServices.Configuration.GetSection("AdLDAP:adIP").Value;
var adUser = AppConfigurtaionServices.Configuration.GetSection("AdLDAP:adUser").Value;
var adPassword = AppConfigurtaionServices.Configuration.GetSection("AdLDAP:adPassword").Value;
var openDomain = AppConfigurtaionServices.Configuration.GetSection("OpenLDAP:openDomain").Value;
var openDomain2 = AppConfigurtaionServices.Configuration.GetSection("OpenLDAP:openDomain2").Value;
var openIP = AppConfigurtaionServices.Configuration.GetSection("OpenLDAP:openIP").Value;
var openUser = AppConfigurtaionServices.Configuration.GetSection("OpenLDAP:openUser").Value;
var openPassword = AppConfigurtaionServices.Configuration.GetSection("OpenLDAP:openPassword").Value;
var companyName = AppConfigurtaionServices.Configuration.GetSection("CompanyName").Value;
companyName = string.IsNullOrEmpty(companyName) ? "XX网络" : companyName;
adHelper = new LdapHelper(adDomain, adDomain2, adIP, adUser, adPassword);
openHelper = new LdapHelper(openDomain, openDomain2, openIP, openUser, openPassword);
DirectoryEntry entry = new DirectoryEntry(adHelper.GetDomainPath(), adUser, adPassword, AuthenticationTypes.Secure);
var companyEntry = new DirectoryEntry();
companyEntry = ExistChildrenOU(entry, companyName);
if (companyEntry == null)
{
companyEntry = entry.Children.Add("OU=" + companyName, "organizationalUnit");
companyEntry.CommitChanges();
}
DirectoryEntry openEntry = new DirectoryEntry(openHelper.GetDomainPath(), openUser, openPassword, AuthenticationTypes.None);
DirectoryEntry openCompanyEntry = ExistChildrenOU(openEntry, companyName);
//getAllUser(openCompanyEntry, companyName);
//同步新增组织
addOrgEntry(companyEntry, openCompanyEntry);
//同步新增、修改、移动用户
addUserEntry(companyEntry, openCompanyEntry, companyName);
//同步禁用离职用户
disUserEntry(companyEntry, openCompanyEntry, companyName);
//同步删除不存在的组织(暂时不考虑删除操作)
//delOrgEntry(companyEntry, openCompanyEntry);
//发送新员工邮件给指定人
if (newEmployees.Count > 0)
{
SendNewEmployeesEmail();
}
//钉钉群机器人预警推送
DingWarning();
}
#region 工具函数
public void DingWarning()
{
try
{
var warningCount = Convert.ToInt32(AppConfigurtaionServices.Configuration.GetSection("Warning:warningCount").Value);
if (W_AddUserCount >= warningCount)
{
//发钉钉机器人
SendDingRobotMsg("【新增提醒】\n过去1分钟内程序同步新增了" + W_AddUserCount + "名员工\n请管理员确认是否存在问题!");
}
if (W_DisUserCount >= warningCount)
{
//发钉钉机器人
SendDingRobotMsg("【禁用提醒】\n过去1分钟内程序同步禁用了" + W_DisUserCount + "名员工\n请管理员确认是否存在问题!");
}
W_AddUserCount = 0;
W_DisUserCount = 0;
var now = DateTime.Now;
if (now.Hour == 17 && now.Minute >= 0 && now.Minute < 5 && I_LastSendTime.ToString("yyyyMMdd") != now.ToString("yyyyMMdd"))
{
//发钉钉机器人
SendDingRobotMsg("【统计提醒】\n" + now.AddDays(-1).ToString("yyyy/MM/dd") + "-" + now.ToString("yyyy/MM/dd") + "\n入职新增" + I_AddUserCount + "人\n跨部门移动" + I_MoveUserCount + "人\n离职禁用" + I_DisUserCount + "人", false);
//更新最后发送时间,一天只发一次
I_LastSendTime = now;
I_AddUserCount = 0;
I_MoveUserCount = 0;
I_DisUserCount = 0;
}
}
catch (Exception ex)
{
log.Error("DingWarning异常", ex);
}
}
/// <summary>
/// 获取openldap所有员工,并构造出一级部门
/// </summary>
/// <param name="openEntry"></param>
/// <param name="companyName"></param>
public void getAllUser(DirectoryEntry openEntry, string companyName)
{
if (openEntry.Children != null)
{
foreach (DirectoryEntry orgChildren in openEntry.Children)
{
if (orgChildren.Name.Contains("cn="))
{
Console.WriteLine(orgChildren.Name);
}
}
foreach (DirectoryEntry orgChildren in openEntry.Children)
{
if (orgChildren.Name.Contains("ou="))
{
getAllUser(orgChildren, companyName);
}
}
}
}
/// <summary>
/// 启用所有被禁用的用户
/// </summary>
/// <param name="adEntry"></param>
/// <param name="companyName"></param>
public void qyUserEntry(DirectoryEntry adEntry, string companyName)
{
try
{
if (adEntry.Children != null)
{
foreach (DirectoryEntry adChildren in adEntry.Children)
{
if (adChildren.Name.Contains("CN=") && adChildren.Properties["userAccountControl"].Value.ToString() != "512")
{
//512是启用
adChildren.Properties["userAccountControl"].Value = 512;
//adChildren.Invoke("SetPassword", new object[] { "abc123!" });
adChildren.CommitChanges();
LogTxtHelper.CreateInLog(adChildren.Name + ",时间:" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
}
}
foreach (DirectoryEntry adChildren in adEntry.Children)
{
if (adChildren.Name.Contains("OU="))
{
qyUserEntry(adChildren, companyName);
}
}
}
}
catch (Exception ex)
{
LogTxtHelper.CreateInLog("qyUserEntry异常,时间:" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + "," + JsonConvert.SerializeObject(ex));
}
}
/// <summary>
/// 添加组织到AD
/// </summary>
/// <param name="entry"></param>
/// <param name="org"></param>
public void addOrgEntry(DirectoryEntry entry, DirectoryEntry openEntry)
{
try
{
if (openEntry.Children != null)
{
foreach (DirectoryEntry orgChildren in openEntry.Children)
{
if (ExistChildrenOU(entry, orgChildren.Name.Replace("ou=", "")) == null && orgChildren.Name.Contains("ou="))
{
var orgEntry = entry.Children.Add(orgChildren.Name, "organizationalUnit");
orgEntry.CommitChanges();
}
}
foreach (DirectoryEntry orgChildren in openEntry.Children)
{
if (orgChildren.Name.Contains("ou="))
{
var childEntry = entry.Children.Find(orgChildren.Name);
addOrgEntry(childEntry, orgChildren);
}
}
}
}
catch (Exception ex)
{
log.Error("addOrgEntry异常", ex);
}
}
/// <summary>
/// 添加、修改、移动用户到AD
/// </summary>
/// <param name="entry"></param>
/// <param name="org"></param>
public void addUserEntry(DirectoryEntry entry, DirectoryEntry openEntry, string companyName)
{
try
{
if (openEntry.Children != null)
{
foreach (DirectoryEntry orgChildren in openEntry.Children)
{
if (orgChildren.Name.Contains("cn="))
{
//ad员工
var domainUser = new DomainUser()
{
UserId = orgChildren.Name.Replace("cn=", ""),
Email = orgChildren.Properties["mail"].Value?.ToString(),
UserName = orgChildren.Properties["displayName"].Value?.ToString(),
Telephone = orgChildren.Properties["mobile"].Value?.ToString(),
Title = orgChildren.Properties["Title"].Value?.ToString()
};
var openPath = orgChildren.Path;
var adUser = adHelper.GetUser(adHelper.GetDomainPath2(companyName), orgChildren.Name.Replace("cn=", ""));
var openPathW = openHelper.GetTailPath(openPath);
//1.如果在ad中存在此用户
if (adUser != null)
{
var adPath = adUser.Path.Replace("\\", "");
var adPathW = adHelper.GetTailPath(adPath);
//1.1.如果open的完整path和ad的完整path相同,则更新
if (adPathW.ToUpper() == openPathW.ToUpper())
{
adPath = adHelper.GetAllPathByTailPath(openPathW, false);
adHelper.EditADAccount(adPath, domainUser);
}
//1.2.如果open的完整path和ad的完整path不相同,则转移到db的path里