今天用C#实现了一套LDAP域账号的创建和查询,感受挺多。
算是第一次接触LDAP吧,之前曾经做了一个登录的验证,就是查询功能,那个相对比较简单,用到了一个方法就搞定了。
这次的需求是要用编程的方式创建域账号,实现域登陆。
首先回顾一下之前查询用到的代码:
public static bool TryAuthenticate(string userName, string password) { string domain = "litb-inc.com"; bool isLogin = false; try { DirectoryEntry entry = new DirectoryEntry(string.Format("LDAP://{0}", domain), userName, password); entry.RefreshCache(); DBLog.Debug("check success"); isLogin = true; } catch (Exception ex) { DBLog.Debug("域验证抛出异常 :" + ex.Message + ex.InnerException); isLogin = false; } return isLogin; }
这是验证指定用户是否在域里认证通过。
接下来,实现创建域账户的操作。在网上找到了一个操作类:
1 using System; 2 using System.Collections; 3 using System.Collections.Generic; 4 using System.Data; 5 using System.DirectoryServices; 6 using System.Linq; 7 using System.Text; 8 using System.Text.RegularExpressions; 9 10 namespace Litb.HRExtension 11 { 12 //静态AD连接类 13 public static class AdHerlp 14 { 15 #region 创建AD连接 16 /// <summary> 17 /// 创建AD连接 18 /// </summary> 19 /// <returns></returns> 20 public static DirectoryEntry GetDirectoryEntry() 21 { 22 DirectoryEntry de = new DirectoryEntry(); 23 de.Path = "LDAP://testhr.com/CN=Users,DC=testhr,DC=com"; 24 de.Username = @"administrator"; 25 de.Password = "litb20!!"; 26 return de; 27 } 28 #endregion 29 30 #region 获取目录实体集合(DomainReference传空即可) 31 /// <summary> 32 /// 33 /// </summary> 34 /// <param name="DomainReference"></param> 35 /// <returns></returns> 36 public static DirectoryEntry GetDirectoryEntry(string DomainReference) 37 { 38 DirectoryEntry entry = new DirectoryEntry("LDAP://testhr.com" + DomainReference, "administrator", "litb20!!", AuthenticationTypes.Secure); 39 return entry; 40 } 41 #endregion 42 } 43 44 //AD操作类 45 public class ADHelper 46 { 47 /// <summary> 48 /// 判断用户是否存在 49 /// </summary> 50 /// <param name="UserName"></param> 51 /// <returns></returns> 52 public bool UserExists(string UserName) 53 { 54 DirectoryEntry de = AdHerlp.GetDirectoryEntry(); 55 DirectorySearcher deSearch = new DirectorySearcher(); 56 deSearch.SearchRoot = de; 57 deSearch.Filter = "(&(objectClass=user) (cn=" + UserName + "))"; 58 SearchResultCollection results = deSearch.FindAll(); 59 if (results.Count == 0) 60 { 61 return false; 62 } 63 else 64 { 65 return true; 66 } 67 } 68 69 /// <summary> 70 /// 创建一个新用户 71 /// </summary> 72 /// <param name="employeeID"></param> 73 /// <param name="name"></param> 74 /// <param name="login"></param> 75 /// <param name="email"></param> 76 /// <param name="group"></param> 77 public void CreateNewUser(string employeeID, string name, string login, string email, string group) 78 { 79 DirectoryEntry de = AdHerlp.GetDirectoryEntry(); 80 81 /// 1. Create user account 82 DirectoryEntries users = de.Children; 83 DirectoryEntry newuser = users.Add("CN=" + login, "user"); 84 85 /// 2. Set properties 86 SetProperty(newuser, "employeeID", employeeID); 87 SetProperty(newuser, "givenname", name); 88 SetProperty(newuser, "SAMAccountName", login); 89 SetProperty(newuser, "userPrincipalName", login); 90 SetProperty(newuser, "mail", email); 91 SetProperty(newuser, "Description", "Create User By HrESS System"); 92 newuser.CommitChanges(); 93 94 /// 3. Set password 95 newuser.AuthenticationType = AuthenticationTypes.Secure; 96 object[] password = new object[] { SetSecurePassword() }; 97 object ret = newuser.Invoke("SetPassword", password); 98 newuser.CommitChanges(); 99 100 //SetPassword(newuser); 101 //newuser.CommitChanges(); 102 103 /// 4. Enable account 104 EnableAccount(newuser); 105 106 /// 5. Add user account to groups 107 AddUserToGroup(de, newuser, group); 108 109 /// 6. Create a mailbox in Microsoft Exchange 110 //GenerateMailBox(login); 111 112 newuser.Close(); 113 de.Close(); 114 } 115 116 /// <summary> 117 /// 修改用户属性 118 /// </summary> 119 /// <param name="de"></param> 120 /// <param name="PropertyName"></param> 121 /// <param name="PropertyValue"></param> 122 public static void SetProperty(DirectoryEntry de, string PropertyName, string PropertyValue) 123 { 124 if (PropertyValue != null) 125 { 126 if (de.Properties.Contains(PropertyName)) 127 { 128 de.Properties[PropertyName][0] = PropertyValue; 129 } 130 else 131 { 132 de.Properties[PropertyName].Add(PropertyValue); 133 } 134 } 135 } 136 137 /// <summary> 138 /// 生成随机密码 139 /// </summary> 140 /// <returns></returns> 141 public string SetSecurePassword() 142 { 143 return "qwe123!@#"; 144 } 145 146 /// <summary> 147 /// 设置用户新密码 148 /// </summary> 149 /// <param name="path"></param> 150 public void SetPassword(DirectoryEntry newuser) 151 { 152 newuser.AuthenticationType = AuthenticationTypes.Secure; 153 object[] password = new object[] { SetSecurePassword() }; 154 object ret = newuser.Invoke("SetPassword", password); 155 newuser.CommitChanges(); 156 newuser.Close(); 157 } 158 159 /// <summary> 160 /// 启用用户帐号 161 /// </summary> 162 /// <param name="de"></param> 163 private static void EnableAccount(DirectoryEntry de) 164 { 165 //UF_DONT_EXPIRE_PASSWD 0x10000 166 int exp = (int)de.Properties["userAccountControl"].Value; 167 de.Properties["userAccountControl"].Value = exp | 0x0001; 168 de.CommitChanges(); 169 //UF_ACCOUNTDISABLE 0x0002 170 int val = (int)de.Properties["userAccountControl"].Value; 171 de.Properties["userAccountControl"].Value = val & ~0x0002; 172 de.CommitChanges(); 173 } 174 175 /// <summary> 176 /// 添加用户到组 177 /// </summary> 178 /// <param name="de"></param> 179 /// <param name="deUser"></param> 180 /// <param name="GroupName"></param> 181 public static void AddUserToGroup(DirectoryEntry de, DirectoryEntry deUser, string GroupName) 182 { 183 DirectorySearcher deSearch = new DirectorySearcher(); 184 deSearch.SearchRoot = de; 185 deSearch.Filter = "(&(objectClass=group) (cn=" + GroupName + "))"; 186 SearchResultCollection results = deSearch.FindAll(); 187 188 bool isGroupMember = false; 189 190 if (results.Count > 0) 191 { 192 DirectoryEntry group = AdHerlp.GetDirectoryEntry(results[0].Path); 193 194 object members = group.Invoke("Members", null); 195 foreach (object member in (IEnumerable)members) 196 { 197 DirectoryEntry x = new DirectoryEntry(member); 198 if (x.Name != deUser.Name) 199 { 200 isGroupMember = false; 201 } 202 else 203 { 204 isGroupMember = true; 205 break; 206 } 207 } 208 209 if (!isGroupMember) 210 { 211 group.Invoke("Add", new object[] { deUser.Path.ToString() }); 212 } 213 group.Close(); 214 } 215 return; 216 } 217 218 /// <summary> 219 /// 禁用一个帐号 220 /// </summary> 221 /// <param name="EmployeeID"></param> 222 public void DisableAccount(string EmployeeID) 223 { 224 DirectoryEntry de = AdHerlp.GetDirectoryEntry(); 225 DirectorySearcher ds = new DirectorySearcher(de); 226 ds.Filter = "(&(objectCategory=Person)(objectClass=user)(employeeID=" + EmployeeID + "))"; 227 ds.SearchScope = SearchScope.Subtree; 228 SearchResult results = ds.FindOne(); 229 230 if (results != null) 231 { 232 DirectoryEntry dey = AdHerlp.GetDirectoryEntry(results.Path); 233 int val = (int)dey.Properties["userAccountControl"].Value; 234 dey.Properties["userAccountControl"].Value = val | 0x0002; 235 dey.Properties["msExchHideFromAddressLists"].Value = "TRUE"; 236 dey.CommitChanges(); 237 dey.Close(); 238 } 239 240 de.Close(); 241 } 242 243 /// <summary> 244 /// 修改用户信息 245 /// </summary> 246 /// <param name="employeeID"></param> 247 /// <param name="department"></param> 248 /// <param name="title"></param> 249 /// <param name="company"></param> 250 public void ModifyUser(string employeeID, string department, string title, string company) 251 { 252 DirectoryEntry de = AdHerlp.GetDirectoryEntry(); 253 DirectorySearcher ds = new DirectorySearcher(de); 254 ds.Filter = "(&(objectCategory=Person)(objectClass=user)(employeeID=" + employeeID + "))"; 255 ds.SearchScope = SearchScope.Subtree; 256 SearchResult results = ds.FindOne(); 257 258 if (results != null) 259 { 260 DirectoryEntry dey = AdHerlp.GetDirectoryEntry(results.Path); 261 SetProperty(dey, "department", department); 262 SetProperty(dey, "title", title); 263 SetProperty(dey, "company", company); 264 dey.CommitChanges(); 265 dey.Close(); 266 } 267 268 de.Close(); 269 } 270 271 /// <summary> 272 /// 检验Email格式是否正确 273 /// </summary> 274 /// <param name="mail"></param> 275 /// <returns></returns> 276 public bool IsEmail(string mail) 277 { 278 Regex mailPattern = new Regex(@"\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*"); 279 return mailPattern.IsMatch(mail); 280 } 281 282 /// <summary> 283 /// 搜索被修改过的用户 284 /// </summary> 285 /// <param name="fromdate"></param> 286 /// <returns></returns> 287 public DataTable GetModifiedUsers(DateTime fromdate) 288 { 289 DataTable dt = new DataTable(); 290 dt.Columns.Add("EmployeeID"); 291 dt.Columns.Add("Name"); 292 dt.Columns.Add("Email"); 293 294 DirectoryEntry de = AdHerlp.GetDirectoryEntry(); 295 DirectorySearcher ds = new DirectorySearcher(de); 296 297 StringBuilder filter = new StringBuilder(); 298 filter.Append("(&(objectCategory=Person)(objectClass=user)(whenChanged>="); 299 filter.Append(ToADDateString(fromdate)); 300 filter.Append("))"); 301 302 ds.Filter = filter.ToString(); 303 ds.SearchScope = SearchScope.Subtree; 304 SearchResultCollection results = ds.FindAll(); 305 306 foreach (SearchResult result in results) 307 { 308 DataRow dr = dt.NewRow(); 309 DirectoryEntry dey = AdHerlp.GetDirectoryEntry(result.Path); 310 dr["EmployeeID"] = dey.Properties["employeeID"].Value; 311 dr["Name"] = dey.Properties["givenname"].Value; 312 dr["Email"] = dey.Properties["mail"].Value; 313 dt.Rows.Add(dr); 314 dey.Close(); 315 } 316 317 de.Close(); 318 return dt; 319 } 320 321 /// <summary> 322 /// 格式化AD的时间 323 /// </summary> 324 /// <param name="date"></param> 325 /// <returns></returns> 326 public string ToADDateString(DateTime date) 327 { 328 string year = date.Year.ToString(); 329 int month = date.Month; 330 int day = date.Day; 331 332 StringBuilder sb = new StringBuilder(); 333 sb.Append(year); 334 if (month < 10) 335 { 336 sb.Append("0"); 337 } 338 sb.Append(month.ToString()); 339 if (day < 10) 340 { 341 sb.Append("0"); 342 } 343 sb.Append(day.ToString()); 344 sb.Append("000000.0Z"); 345 return sb.ToString(); 346 } 347 } 348 }
有了这个操作类,就可以进行域账号的创建了,调用示例:
Console.WriteLine("Begin CreateNewUser"); string name = "wj" + System.Guid.NewGuid().ToString().Substring(0, 5); string id = System.Guid.NewGuid().ToString().Substring(0, 5);my.CreateNewUser(id, name, name, name + "@testhr.com", "testhr.com/Users"); Console.WriteLine("域用户名创建成功:" + name);
注意域账号的用户名不能有类似-,下划线之类的特殊字符。
在最初尝试的时候,创建对象 DirectoryEntry的时候总是有问题,最终这两种方式都是有效的:
DirectoryEntry de = new DirectoryEntry();
de.Path = "LDAP://testhr.com/CN=Users,DC=testhr,DC=com";
de.Username = @"administrator";
de.Password = "litb20!!";
return de;
DirectoryEntry entry = new DirectoryEntry("LDAP://testhr.com", "administrator", "litb20!!", AuthenticationTypes.Secure);
return entry;
其次,在创建完用户以后,需要设置用户的密码,这个方法总是报错,后来经过检查,发现如果只传递path字符串,是不行的,必须操作现有对象的Invoke方法才可以!
或者传递对象引用。
最终,成功创建了域账户。
在测试的时候,同一台机器加入了多个账号后,就会有问题,报出类似这样的错误:
最终,可以通过在服务器上删除这台电脑的方式来解决,或者重命名本地计算机名称。
当程序放在服务器上时,又报了错误:
Info:该服务器不可操作。
在 System.DirectoryServices.DirectoryEntry.Bind(Boolean throwIfFail)
在 System.DirectoryServices.DirectoryEntry.Bind()
在 System.DirectoryServices.DirectoryEntry.get_IsContainer()
在 System.DirectoryServices.DirectoryEntries.CheckIsContainer()
在 System.DirectoryServices.DirectoryEntries.Add(String name, String schemaClassName)
在 Litb.HRExtension.ADHelper.CreateNewUser(String name, String password, String firstname, String lastname, String email, String group)
位置 f:\code\HrExtension\Litb.HRExtension\Litb.HRExtension\ADHelper.cs:行号 87
在 ConsoleTest.Program.Main(String[] args) 位置 f:\code\HrExtension\Litb.HRExtension\ConsoleTest\Program.cs:行号 42
最终找到的原因是服务器win2003的DNS设为了一个固定ip,而这台机器没有对litb-inc.com的解析。
最终修改了DNS配置,问题解决。