JAVA使用Ldap操作AD域的方法示例
項目上遇到的需要在集成 操作域用戶的信息的功能,第一次接觸ad域,因為不了解而且網(wǎng)上其他介紹不明確,比較費時,這里記錄下。
說明:
(1). 特別注意:Java操作查詢域用戶信息獲取到的數(shù)據(jù)和域管理員在電腦上操作查詢的數(shù)據(jù)可能會存在差異(同一個意思的表示字段,兩者可能不同)。
(2). 連接ad域有兩個地址: ldap://XXXXX.com:389 和 ldap://XXXXX.com:636(SSL)。
(3). 端口389用于一般的連接,例如登錄,查詢等非密碼操作,端口636安全性較高,用戶密碼相關(guān)操作,例如修改密碼等。
(4). 域控可能有多臺服務(wù)器,之間數(shù)據(jù)同步不及時,可能會導(dǎo)致已經(jīng)修改的數(shù)據(jù)被覆蓋掉,這個要么域控縮短同步的時間差,要么同時修改每一臺服務(wù)器的數(shù)據(jù)。
1. 389登錄
// 只要不拋出異常就是驗證通過
public LdapContext adLogin(JSONObject json) {
String username = json.getString("username");
String password = json.getString("password");
String server = "ldap://XXXXXXX.com:389";
try {
Hashtable<String, String> env = new Hashtable<String, String>();
//用戶名稱,cn,ou,dc 分別:用戶,組,域
env.put(Context.SECURITY_PRINCIPAL, username);
//用戶密碼 cn 的密碼
env.put(Context.SECURITY_CREDENTIALS, password);
//url 格式:協(xié)議://ip:端口/組,域 ,直接連接到域或者組上面
env.put(Context.PROVIDER_URL, server);
//LDAP 工廠
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
//驗證的類型 "none", "simple", "strong"
env.put(Context.SECURITY_AUTHENTICATION, "simple");
LdapContext ldapContext = new InitialLdapContext(env, null);
log.info("ldapContext:" + ldapContext);
log.info("用戶" + username + "登錄驗證成功");
return ldapContext;
} catch (NamingException e) {
log.info("用戶" + username + "登錄驗證失敗");
log.info("錯誤信息:"+e.getExplanation());
return null;
}
}
2. 636登錄驗證(需要導(dǎo)入證書)
//證書提前倒入的Java庫中
// 參考:https://www.cnblogs.com/moonson/p/4454159.html
LdapContext adLoginSSL(JSONObject json) {
String username = json.getString("username");
String password = json.getString("password");
Hashtable env = new Hashtable();
String javaHome = System.getProperty("java.home");
String keystore = javaHome+"/lib/security/cacerts";
log.info("java.home,{}",keystore);
// 加載導(dǎo)入jdk的域證書
System.setProperty("javax.net.ssl.trustStore", keystore);
System.setProperty("javax.net.ssl.trustStorePassword", "changeit");
String LDAP_URL = "ldap://XXXXXX.com:636"; // LDAP訪問地址
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
env.put(Context.SECURITY_PROTOCOL, "ssl");//鏈接認(rèn)證服務(wù)器
env.put(Context.PROVIDER_URL, LDAP_URL);
env.put(Context.SECURITY_AUTHENTICATION, "simple");
env.put(Context.SECURITY_PRINCIPAL, username);
env.put(Context.SECURITY_CREDENTIALS, password);
try {
LdapContext ldapContext = new InitialLdapContext(env, null);
log.info("認(rèn)證成功");// 這里可以改成異常拋出。
return ldapContext;
} catch (javax.naming.AuthenticationException e) {
log.info("認(rèn)證失敗:{}",e.getMessage());
} catch (Exception e) {
log.info("認(rèn)證出錯:{}",e.getMessage());
}
return null;
}
3. 查詢域用戶信息
public List getUserKey(JSONObject json){
JSONObject admin = new JSONObject();
admin.put("username","Aaaaa");
admin.put("password", "bbbbbbbb");
String name = json.getString("name");
log.info("需要查詢的ad信息:{}",name);
List<JSONObject> resultList = new JSONArray();
LdapContext ldapContext = adLogin(admin); //連接到域控
if (ldapContext!=null){
String company = "";
String result = "";
try {
// 域節(jié)點
String searchBase = "DC=XXXXXXX,DC=com";
// LDAP搜索過濾器類
//cn=*name*模糊查詢
//cn=name 精確查詢
// String searchFilter = "(objectClass="+type+")";
String searchFilter = "(sAMAccountName="+name+")"; //查詢域帳號
// 創(chuàng)建搜索控制器
SearchControls searchCtls = new SearchControls();
String returnedAtts[]={"description","sAMAccountName","userAccountControl"};
searchCtls.setReturningAttributes(returnedAtts); //設(shè)置指定返回的字段,不設(shè)置則返回全部
// 設(shè)置搜索范圍 深度
searchCtls.setSearchScope(SearchControls.SUBTREE_SCOPE);
// 根據(jù)設(shè)置的域節(jié)點、過濾器類和搜索控制器搜索LDAP得到結(jié)果
NamingEnumeration answer = ldapContext.search(searchBase, searchFilter,searchCtls);
// 初始化搜索結(jié)果數(shù)為0
int totalResults = 0;
int rows = 0;
while (answer.hasMoreElements()) {// 遍歷結(jié)果集
SearchResult sr = (SearchResult) answer.next();// 得到符合搜索條件的DN
++rows;
String dn = sr.getName();
log.info(dn);
Attributes Attrs = sr.getAttributes();// 得到符合條件的屬性集
if (Attrs != null) {
try {
for (NamingEnumeration ne = Attrs.getAll(); ne.hasMore();) {
Attribute Attr = (Attribute) ne.next();// 得到下一個屬性
// 讀取屬性值
for (NamingEnumeration e = Attr.getAll(); e.hasMore(); totalResults++) {
company = e.next().toString();
JSONObject tempJson = new JSONObject();
tempJson.put(Attr.getID(), company.toString());
resultList.add(tempJson);
}
}
} catch (NamingException e) {
log.info("Throw Exception : " + e.getMessage());
}
}
}
log.info("總共用戶數(shù):" + rows);
} catch (NamingException e) {
log.info("Throw Exception : " + e.getMessage());
}finally {
try{
ldapContext.close();
}catch (Exception e){
e.printStackTrace();
}
}
}
return resultList;
}
4. 重置用戶密碼
// 管理員重置用戶密碼,后強制用戶首次登錄修改密碼
public Map<String, String> updateAdPwd(JSONObject json) {
String dn = json.getString("dn");//要修改的帳號(這個dn是查詢的用戶信息里的dn的值,而不是域賬號)
String password = json.getString("password");//新密碼
JSONObject admin = new JSONObject();
admin.put("username","aaaaaaa");
admin.put("password", "bbbbbbb");
Map<String,String> map = new HashMap<String,String>();
LdapContext ldapContext = adLoginSSL(admin); //連接636端口域
ModificationItem[] mods = new ModificationItem[2];
if (ldapContext!=null){
try {
String newQuotedPassword = "\"" + password + "\"";
byte[] newUnicodePassword = newQuotedPassword.getBytes("UTF-16LE");
// unicodePwd:修改的字段,newUnicodePassword:修改的值
mods[0] = new ModificationItem(DirContext.REPLACE_ATTRIBUTE,
new BasicAttribute("unicodePwd", newUnicodePassword));
mods[1] = new ModificationItem(DirContext.REPLACE_ATTRIBUTE,
new BasicAttribute("pwdLastSet", "0")); // 首次登錄必須修改密碼
// 修改密碼
ldapContext.modifyAttributes(dn, mods);
map.put("result", "S");
map.put("message","成功");
}catch (Exception e){
map.put("result","E");
map.put("message", "無法重置密碼");
}finally {
try{
ldapContext.close();
}catch (Exception e){
e.printStackTrace();
}
}
}else {
log.info("");
map.put("result","E");
map.put("message", "驗證失敗");
}
return map;
}
5. 域賬號解鎖
// 表示鎖定的字段需要測試,不一定這個lockoutTime
public Map<String, String> deblocking(JSONObject json) {
JSONObject admin = new JSONObject();
String dn = json.getString("dn"); //被解鎖的帳號(這個dn指的是查詢用戶信息里的dn的值,不是域賬號)
admin.put("username","aaaaaa");
admin.put("password","bbbbbb");
Map<String,String> map = new HashMap<String,String>();
LdapContext ldapContext = adLogin(admin);
ModificationItem[] mods = new ModificationItem[1];
if (ldapContext!=null){
try {
// "0" 表示未鎖定,不為0表示鎖定
mods[0] = new ModificationItem(DirContext.REPLACE_ATTRIBUTE,
new BasicAttribute("lockoutTime","0"));
// 解鎖域帳號
ldapContext.modifyAttributes(dn, mods);
map.put("result", "S");
map.put("message","成功");
}catch (Exception e){
map.put("result","E");
map.put("message", "解鎖失敗");
}finally {
try{
ldapContext.close();
}catch (Exception e){
e.printStackTrace();
}
}
}else {
map.put("result","E");
map.put("message", "驗證失敗");
}
return map;
}
Java通過Ldap操作AD的增刪改查詢
package com.smnpc.util;
import java.util.Hashtable;
import java.util.Vector;
import javax.naming.Context;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.directory.Attribute;
import javax.naming.directory.Attributes;
import javax.naming.directory.BasicAttribute;
import javax.naming.directory.BasicAttributes;
import javax.naming.directory.DirContext;
import javax.naming.directory.InitialDirContext;
import javax.naming.directory.ModificationItem;
import javax.naming.directory.SearchControls;
import javax.naming.directory.SearchResult;
import javax.naming.ldap.LdapContext;
/**
* Java通過Ldap操作AD的增刪該查詢
* @author guob
*/
public class LdapbyUser {
DirContext dc = null;
String root = "dc=example,dc=com"; // LDAP的根節(jié)點的DC
/**
*
* @param dn類似于"CN=RyanHanson,dc=example,dc=com"
* @param employeeID是Ad的一個員工號屬性
*/
public LdapbyUser(String dn,String employeeID) {
init();
// add();//添加節(jié)點
// delete("ou=hi,dc=example,dc=com");//刪除"ou=hi,dc=example,dc=com"節(jié)點
// renameEntry("ou=new,o=neworganization,dc=example,dc=com","ou=neworganizationalUnit,o=neworganization,dc=example,dc=com");//重命名節(jié)點"ou=new,o=neworganization,dc=example,dc=com"
// searchInformation("dc=example,dc=com", "", "sAMAccountName=guob");//遍歷所有根節(jié)點
modifyInformation(dn,employeeID);//修改
// Ldapbyuserinfo("guob");//遍歷指定節(jié)點的分節(jié)點
close();
}
/**
*
* Ldap連接
*
* @return LdapContext
*/
public void init() {
Hashtable env = new Hashtable();
String LDAP_URL = "ldap://xxxx:389"; // LDAP訪問地址
String adminName = "example\\user"; // 注意用戶名的寫法:domain\User或
String adminPassword = "userpassword"; // 密碼
env.put(Context.INITIAL_CONTEXT_FACTORY,
"com.sun.jndi.ldap.LdapCtxFactory");
env.put(Context.PROVIDER_URL, LDAP_URL);
env.put(Context.SECURITY_AUTHENTICATION, "simple");
env.put(Context.SECURITY_PRINCIPAL, adminName);
env.put(Context.SECURITY_CREDENTIALS, adminPassword);
try {
dc = new InitialDirContext(env);// 初始化上下文
System.out.println("認(rèn)證成功");// 這里可以改成異常拋出。
} catch (javax.naming.AuthenticationException e) {
System.out.println("認(rèn)證失敗");
} catch (Exception e) {
System.out.println("認(rèn)證出錯:" + e);
}
}
/**
* 添加
*/
public void add(String newUserName) {
try {
BasicAttributes attrs = new BasicAttributes();
BasicAttribute objclassSet = new BasicAttribute("objectClass");
objclassSet.add("sAMAccountName");
objclassSet.add("employeeID");
attrs.put(objclassSet);
attrs.put("ou", newUserName);
dc.createSubcontext("ou=" + newUserName + "," + root, attrs);
} catch (Exception e) {
e.printStackTrace();
System.out.println("Exception in add():" + e);
}
}
/**
* 刪除
*
* @param dn
*/
public void delete(String dn) {
try {
dc.destroySubcontext(dn);
} catch (Exception e) {
e.printStackTrace();
System.out.println("Exception in delete():" + e);
}
}
/**
* 重命名節(jié)點
*
* @param oldDN
* @param newDN
* @return
*/
public boolean renameEntry(String oldDN, String newDN) {
try {
dc.rename(oldDN, newDN);
return true;
} catch (NamingException ne) {
System.err.println("Error: " + ne.getMessage());
return false;
}
}
/**
* 修改
*
* @return
*/
public boolean modifyInformation(String dn,String employeeID) {
try {
System.out.println("updating...\n");
ModificationItem[] mods = new ModificationItem[1];
/* 修改屬性 */
// Attribute attr0 = new BasicAttribute("employeeID", "W20110972");
// mods[0] = new ModificationItem(DirContext.REPLACE_ATTRIBUTE, attr0);
/* 刪除屬性 */
// Attribute attr0 = new BasicAttribute("description",
// "陳軼");
// mods[0] = new ModificationItem(DirContext.REMOVE_ATTRIBUTE,
// attr0);
/* 添加屬性 */
Attribute attr0 = new BasicAttribute("employeeID",employeeID);
mods[0] = new ModificationItem(DirContext.ADD_ATTRIBUTE, attr0);
/* 修改屬性 */
dc.modifyAttributes(dn+",dc=example,dc=com", mods);
return true;
} catch (NamingException e) {
e.printStackTrace();
System.err.println("Error: " + e.getMessage());
return false;
}
}
/**
* 關(guān)閉Ldap連接
*/
public void close() {
if (dc != null) {
try {
dc.close();
} catch (NamingException e) {
System.out.println("NamingException in close():" + e);
}
}
}
/**
* @param base :根節(jié)點(在這里是"dc=example,dc=com")
* @param scope :搜索范圍,分為"base"(本節(jié)點),"one"(單層),""(遍歷)
* @param filter :指定子節(jié)點(格式為"(objectclass=*)",*是指全部,你也可以指定某一特定類型的樹節(jié)點)
*/
public void searchInformation(String base, String scope, String filter) {
SearchControls sc = new SearchControls();
if (scope.equals("base")) {
sc.setSearchScope(SearchControls.OBJECT_SCOPE);
} else if (scope.equals("one")) {
sc.setSearchScope(SearchControls.ONELEVEL_SCOPE);
} else {
sc.setSearchScope(SearchControls.SUBTREE_SCOPE);
}
NamingEnumeration ne = null;
try {
ne = dc.search(base, filter, sc);
// Use the NamingEnumeration object to cycle through
// the result set.
while (ne.hasMore()) {
System.out.println();
SearchResult sr = (SearchResult) ne.next();
String name = sr.getName();
if (base != null && !base.equals("")) {
System.out.println("entry: " + name + "," + base);
} else {
System.out.println("entry: " + name);
}
Attributes at = sr.getAttributes();
NamingEnumeration ane = at.getAll();
while (ane.hasMore()) {
Attribute attr = (Attribute) ane.next();
String attrType = attr.getID();
NamingEnumeration values = attr.getAll();
Vector vals = new Vector();
// Another NamingEnumeration object, this time
// to iterate through attribute values.
while (values.hasMore()) {
Object oneVal = values.nextElement();
if (oneVal instanceof String) {
System.out.println(attrType + ": " + (String) oneVal);
} else {
System.out.println(attrType + ": " + new String((byte[]) oneVal));
}
}
}
}
} catch (Exception nex) {
System.err.println("Error: " + nex.getMessage());
nex.printStackTrace();
}
}
/**
* 查詢
*
* @throws NamingException
*/
public void Ldapbyuserinfo(String userName) {
// Create the search controls
SearchControls searchCtls = new SearchControls();
// Specify the search scope
searchCtls.setSearchScope(SearchControls.SUBTREE_SCOPE);
// specify the LDAP search filter
String searchFilter = "sAMAccountName=" + userName;
// Specify the Base for the search 搜索域節(jié)點
String searchBase = "DC=example,DC=COM";
int totalResults = 0;
String returnedAtts[] = { "url", "whenChanged", "employeeID", "name",
"userPrincipalName", "physicalDeliveryOfficeName",
"departmentNumber", "telephoneNumber", "homePhone", "mobile",
"department", "sAMAccountName", "whenChanged", "mail" }; // 定制返回屬性
searchCtls.setReturningAttributes(returnedAtts); // 設(shè)置返回屬性集
// searchCtls.setReturningAttributes(null); // 不定制屬性,將返回所有的屬性集
try {
NamingEnumeration answer = dc.search(searchBase, searchFilter,
searchCtls);
if (answer == null || answer.equals(null)) {
System.out.println("answer is null");
} else {
System.out.println("answer not null");
}
while (answer.hasMoreElements()) {
SearchResult sr = (SearchResult) answer.next();
System.out
.println("************************************************");
System.out.println("getname=" + sr.getName());
Attributes Attrs = sr.getAttributes();
if (Attrs != null) {
try {
for (NamingEnumeration ne = Attrs.getAll(); ne
.hasMore();) {
Attribute Attr = (Attribute) ne.next();
System.out.println("AttributeID="
+ Attr.getID().toString());
// 讀取屬性值
for (NamingEnumeration e = Attr.getAll(); e
.hasMore(); totalResults++) {
String user = e.next().toString(); // 接受循環(huán)遍歷讀取的userPrincipalName用戶屬性
System.out.println(user);
}
// System.out.println(" ---------------");
// // 讀取屬性值
// Enumeration values = Attr.getAll();
// if (values != null) { // 迭代
// while (values.hasMoreElements()) {
// System.out.println(" 2AttributeValues="
// + values.nextElement());
// }
// }
// System.out.println(" ---------------");
}
} catch (NamingException e) {
System.err.println("Throw Exception : " + e);
}
}
}
System.out.println("Number: " + totalResults);
} catch (Exception e) {
e.printStackTrace();
System.err.println("Throw Exception : " + e);
}
}
/**
* 主函數(shù)用于測試
* @param args
*/
public static void main(String[] args) {
new LdapbyUser("CN=RyanHanson","bbs.it-home.org");
}
}
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
詳解SpringCloud-Alibaba-Seata分布式事務(wù)
這篇文章主要介紹了SpringCloud-Alibaba-Seata分布式事務(wù)的相關(guān)知識,本文給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-12-12
Spring?BOOT?AOP基礎(chǔ)應(yīng)用教程
這篇文章主要介紹了Spring?BOOT?AOP的使用,文章從相關(guān)問題展開全文內(nèi)容詳情,具有一定的參考價值,需要的小伙伴可以參考一下2022-07-07
java中VO和DTO之間的轉(zhuǎn)換實現(xiàn)
本文主要介紹了java中VO和DTO之間的轉(zhuǎn)換實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-05-05
Java中將異步調(diào)用轉(zhuǎn)為同步的五種實現(xiàn)方法
本文介紹了將異步調(diào)用轉(zhuǎn)為同步阻塞模式的五種方法:wait/notify、ReentrantLock+Condition、Future、CountDownLatch和CyclicBarrier,每種方法都有其適用場景和核心機制,可以根據(jù)具體需求選擇合適的方法,需要的朋友可以參考下2025-02-02
java?spring?validation?自動、手動校驗
HibernateValidator簡化了Java開發(fā)中的參數(shù)校驗過程,提供自動和手動兩種校驗方式,通過引入相關(guān)依賴并使用@Validated注解,可以實現(xiàn)自動校驗,手動校驗則需要使用ValidatorUtils類,此方法有效減少代碼重復(fù),提高開發(fā)效率2024-09-09
利用Java實現(xiàn)動態(tài)加載數(shù)據(jù)庫
這篇文章主要為大家詳細介紹了一個java小案例,即動態(tài)加載數(shù)據(jù)庫信息,文中的示例代碼簡潔易懂,具有一定的學(xué)習(xí)價值,感興趣的小伙伴可以了解一下2023-10-10

