一较曼、什么是LDAP?
(一)在介紹什么是LDAP之前振愿,我們先來復習一個東西:“什么是目錄服務捷犹?”
1. 目錄服務是一個特殊的數(shù)據(jù)庫弛饭,用來保存描述性的、基于屬性的詳細信息萍歉,支持過濾功能侣颂。
2. 是動態(tài)的,靈活的枪孩,易擴展的憔晒。
如:人員組織管理,電話簿蔑舞,地址簿拒担。
(二)了解完目錄服務后,我們再來看看LDAP的介紹:
LDAP(Light Directory Access Portocol)攻询,它是基于X.500標準的輕量級目錄訪問協(xié)議从撼。
目錄是一個為查詢、瀏覽和搜索而優(yōu)化的數(shù)據(jù)庫钧栖,它成樹狀結(jié)構(gòu)組織數(shù)據(jù)低零,類似文件目錄一樣。
目錄數(shù)據(jù)庫和關(guān)系數(shù)據(jù)庫不同桐经,它有優(yōu)異的讀性能,但寫性能差浙滤,并且沒有事務處理阴挣、回滾等復雜功能,不適于存儲修改頻繁的數(shù)據(jù)纺腊。所以目錄天生是用來查詢的畔咧,就好象它的名字一樣。
LDAP目錄服務是由目錄數(shù)據(jù)庫和一套訪問協(xié)議組成的系統(tǒng)揖膜。
(三)為什么要使用
LDAP是開放的Internet標準誓沸,支持跨平臺的Internet協(xié)議,在業(yè)界中得到廣泛認可的壹粟,并且市場上或者開源社區(qū)上的大多產(chǎn)品都加入了對LDAP的支持拜隧,因此對于這類系統(tǒng),不需單獨定制趁仙,只需要通過LDAP做簡單的配置就可以與服務器做認證交互洪添。“簡單粗暴”雀费,可以大大降低重復開發(fā)和對接的成本干奢。
(四)相關(guān)屬性說明
屬性名稱 | 含義 | 備注 |
---|---|---|
OU | Organizational Unit(組織單元) | 最多可以有四級,每級最長 32 個字符盏袄,可以為中文忿峻。 |
DC | Domain Component(域名) | dc=xxxx,dc=com |
CN | Common Name(用戶名或服務器名) | 最長可以到 80 個字符薄啥,可以為中文。 |
O | Organization(組織名稱) | 可以 3~64 個字符長度逛尚。 |
C | Country(國家名) | 可選垄惧,為 2 個字符長度。 |
SN | 姓 | |
UID | 用戶ID | |
DN | 唯一標識 | 類似于 Linux 文件系統(tǒng)的絕對路徑黑低,每個對象都有一個唯一的名稱赘艳。 |
二、如何訪問LDAP克握?
Softerra LDAP Browser(LDAP客戶端工具) 4.5 官方版
三蕾管、java 調(diào)用范例
pom.xml
<dependency>
<groupId>org.springframework.ldap</groupId>
<artifactId>spring-ldap-core</artifactId>
<version>2.3.2.RELEASE</version>
</dependency>
/**
* 初始化LdapTemplate
*
* @return
*/
private LdapTemplate getLdapTemplate(String url, String base, String userDn, String password) {
if (template == null) {
try {
LdapContextSource contextSource = new LdapContextSource();
contextSource.setUrl(url);
contextSource.setBase(base);
contextSource.setUserDn(userDn);
contextSource.setPassword(password);
contextSource.setPooled(false);
contextSource.afterPropertiesSet(); // important
template = new LdapTemplate(contextSource);
} catch (Exception e) {
e.printStackTrace();
}
}
return template;
}
/**
* 根據(jù)用戶uid查詢ldap用戶信息
*
* @return
*/
private LdapUserInfo searchLdapUerInfo(String uid) {
// String url = "ldap://10.10.31.14:389/";
// String base = "dc=authldap,dc=edu,dc=cn";
// String userDn = "uid=ldap_gh,ou=Manager,dc=authldap,dc=edu,dc=cn";
// String password = "zut_gh_2018@";
String url = redisOpsUtil.getValue("ldap_url");
String base = redisOpsUtil.getValue("ldap_base");
String userDn = redisOpsUtil.getValue("ldap_user_dn");
String password = redisOpsUtil.getValue("ldap_password");
try {
LdapTemplate template = this.getLdapTemplate(url, base, userDn, password);
String filter = "(&(objectclass=person)(uid=" + uid + "))";
List<LdapUserInfo> foundLdapUserList = template.search("ou=People", filter, new AttributesMapper() {
@Override
public Object mapFromAttributes(Attributes attributes) throws NamingException {
LdapUserInfo ldapUserInfo = new LdapUserInfo();
Attribute a = attributes.get("cn");
if (a != null) {
ldapUserInfo.setCn((String) a.get());
}
a = attributes.get("uid");
if (a != null) {
ldapUserInfo.setUid((String) a.get());
}
a = attributes.get("userPassword");
if (a != null) {
ldapUserInfo.setUserPassword(new String((byte[]) a.get()));
}
return ldapUserInfo;
}
});
if (!CollectionUtils.isEmpty(foundLdapUserList)) {
return foundLdapUserList.get(0);
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* 用于用戶密碼在LDAP進行驗證
*
* @param ldappw LDAP中取出的用戶密碼
* @param inputpw 用戶輸入的用戶密碼
* @return 是否驗證通過
* @throws NoSuchAlgorithmException
*/
private boolean verifySHA(String ldappw, String inputpw)
throws NoSuchAlgorithmException {
// MessageDigest 提供了消息摘要算法,如 MD5 或 SHA菩暗,的功能掰曾,這里LDAP使用的是SHA-1
MessageDigest md = MessageDigest.getInstance("SHA-1");
// 取出加密字符
if (ldappw.startsWith("{SSHA}")) {
ldappw = ldappw.substring(6);
} else if (ldappw.startsWith("{SHA}")) {
ldappw = ldappw.substring(5);
}
// 解碼BASE64
byte[] ldappwbyte = Base64.decode(ldappw.getBytes());
byte[] shacode;
byte[] salt;
// 前20位是SHA-1加密段,20位后是最初加密時的隨機明文
if (ldappwbyte.length <= 20) {
shacode = ldappwbyte;
salt = new byte[0];
} else {
shacode = new byte[20];
salt = new byte[ldappwbyte.length - 20];
System.arraycopy(ldappwbyte, 0, shacode, 0, 20);
System.arraycopy(ldappwbyte, 20, salt, 0, salt.length);
}
// 把用戶輸入的密碼添加到摘要計算信息
md.update(inputpw.getBytes());
// 把隨機明文添加到摘要計算信息
md.update(salt);
// 按SSHA把當前用戶密碼進行計算
byte[] inputpwbyte = md.digest();
// 返回校驗結(jié)果
return MessageDigest.isEqual(shacode, inputpwbyte);
}
/**
* 校驗ldap登陸密碼
*
* @param userName
* @param password
* @return
* @throws NoSuchAlgorithmException
*/
private boolean checkLdapLogin(String userName, String password) throws NoSuchAlgorithmException {
LdapUserInfo ldapUserInfo = searchLdapUerInfo(userName);
if (ldapUserInfo != null) {
return this.verifySHA(ldapUserInfo.getUserPassword(), password);
}
return false;
}
三停团、python3 調(diào)用范例
from ldap3 import Server, Connection, ALL, SUBTREE
import pymysql
LDAP_SERVER_URL = '10.10.1.155:1389'
ADMIN_DN = "uid=adminread,ou=People,dc=zzptc,dc=com"
ADMIN_PASSWORD = "adminread"
SEARCH_BASE = "dc=zzptc,dc=com"
DB_URL = "10.10.1.88"
DB_USER_NAME = "root"
DB_USER_PWD = "JYKJ168@joywise.net"
def get_ldap_users():
print("======get_ldap_users=======")
db = pymysql.connect(DB_URL, DB_USER_NAME, DB_USER_PWD, "base-platform")
cursor = db.cursor()
sql = "select userName from t_user "
cursor.execute(sql)
user_name_list = cursor.fetchall()
cursor.close()
db.close()
server = Server(LDAP_SERVER_URL, get_info=ALL)
conn = Connection(server, ADMIN_DN, ADMIN_PASSWORD, auto_bind=True)
conn.open()
conn.bind()
user_list = []
for user_name in user_name_list:
filter_condition = '(&(objectclass=person)(uid=%s))' % user_name
filter_attributes = ['alias-list', 'uid', 'cn', 'userPassword']
res = conn.search(SEARCH_BASE, filter_condition, attributes=filter_attributes)
if res:
entry = conn.entries[0]
u = {'uid': entry['uid'], 'userPassword': str(entry['userPassword'])[2:-1],
'alias-list': entry['alias-list'], 'cn': entry['cn']}
print(u)
user_list.append(u)
return user_list