该项目提供了一个快速的 OpenLDAP 类包装器,可以访问 OpenLDAP 服务器和 Windows Active Directory 服务器。
此包使用 Swift Package Manager 构建,是 Perfect 项目的一部分。
请确保您已安装并激活最新的 Swift 4.0 工具链。
注意: 由于 LDAP 广泛应用于具有各种不同实现的操作系统中,因此标记有 (
将以下依赖项添加到您项目的 Package.swift 文件中
.package(url: "https://github.com/PerfectlySoft/Perfect-LDAP.git",
from: "3.0.0")
// in the target section:
depedencies: ["PerfectLDAP"]
然后将 PerfectLDAP 导入到您的源代码中
import PerfectLDAP
您可以根据需要创建带有或不带有登录凭据的实际连接。 完整的 API 是 LDAP(url:String, loginData: Login?, codePage: Iconv.CodePage)
。 codePage
选项适用于那些应用了 .UTF8 以外字符集的服务器,例如,设置 codePage: .GB2312
以连接到简体中文的 LDAP 服务器。
PerfectLDAP 提供了 TLS 选项以考虑网络安全,即,您可以选择 ldap://
或 ldaps://
进行连接,如下演示所示
// this will connect to a 389 port without any encryption
let ld = try LDAP(url: "ldap://perfect.com")
或者,
// this will connect to a 636 port with certificates
let ld = try LDAP(url: "ldaps://perfect.com")
连接后,可以为 LDAP 对象设置超时选项,时间单位为秒
// set the timeout for communication. In this example, connection will be timeout in ten seconds.
connection.timeout = 10
许多服务器强制要求在执行任何实际 LDAP 操作之前进行登录,但是,PerfectLDAP 提供了多个登录选项,如下演示所示
// this snippet demonstrate how to connect to LDAP server with a login credential
// NOTE: this kind of connection will block the thread until server return or timeout.
// create login credential
let credential = LDAP.login( ... )
let connection = try LDAP(url: "ldaps://...", loginData: login)
除了上述同步登录选项外,一个两阶段的线程登录过程也可以为应用程序带来更多的控制
// first create a connection
let connection = try LDAP(url: "ldaps:// ...")
// setup login info
let credential = LDAP.login( ... )
// login in a separated thread
connection.login(info: credential) { err in
// if err is not nil, then something must be wrong in the login process.
}
PerfectLDAP 提供了一个名为 LDAP.Login
的特殊对象,用于存储 LDAP 连接的基本帐户信息,构造函数的形式取决于身份验证类型
要使用简单的登录方法,只需调用 LDAP.login(binddn: String, password: String)
,如下面的代码片段所示
let credential = LDAP.Login(binddn: "CN=judy,CN=Users,DC=perfect,DC=com", password: "0penLDAP")
要应用 GSSAPI 身份验证,请调用 LDAP.login(user:String, mechanism: AuthType)
来构造登录凭据(假设用户已经获得了有效的票证)
// this call will generate a GSSAPI login credential
let credential = LDAP.login(user: "judy", mechanism: .GSSAPI)
要应用其他 SASL 机制,例如 GSS-SPNEGO 和 Digest-MD5 交互式登录,请调用 LDAP.login(authname: String, user: String, password: String, realm: String, mechanism: AuthType)
,如下演示所示
// apply DIGEST-MD5 mechanism.
let credential = LDAP.Login(authname: "judy", user: "DN:CN=judy,CN=Users,DC=perfect,DC=com", password: "0penLDAP", realm: "PERFECT.COM", mechanism: .DIGEST)
authname
相当于 SASL_CB_AUTHNAME
,而 user
实际上是 SASL_CB_USER
的宏。 如果上述任何参数不适用于您的情况,只需分配一个空字符串 "" 即可忽略它。
PerfectLDAP 提供了具有相同参数的搜索 API 的异步和同步版本
同步搜索将阻塞线程,直到服务器返回,完整的 api 是 LDAP.search(base:String, filter:String, scope:Scope, attributes: [String], sortedBy: String) throws -> [String:[String:Any]]
。 这是一个例子
// perform an ldap search synchronously, which will return a full set of attributes
// with a natural (unsorted) order, in form of a dictionary.
let res = try connection.search(base: "CN=Users,DC=perfect,DC=com", filter:"(objectclass=*)")
print(res)
异步搜索允许在独立的线程中执行搜索。 完成后,线程将通过字典中的结果集回调。 异步搜索的完整 api 是 LDAP.search(base:String, filter:String, scope:Scope, attributes: [String], sortedBy: String, completion: @escaping ([String:[String:Any]])-> Void)
。 等效的例子是
// perform an ldap search asynchronously, which will return a full set of attributes
// with a natural (unsorted) order, in form of a dictionary.
connection.search(base: "CN=Users,DC=perfect,DC=com", filter:"(objectclass=*)") {
res in
print(res)
}
"(objectclass=*)"
,表示所有可能的结果LDAP.sortingString()
生成sortedBy
参数是一个字符串,指示远程服务器执行具有排序集的搜索。 PerfectLDAP 提供了一种更口头的方式来构建这样的字符串,即,一个元组数组来描述哪些属性将控制结果集
// each tuple consists two parts: the sorting field and its order - .ASC or .DSC
let sort = LDAP.sortingString(sortedBy: [("description", .ASC)])
连接后,可以为 LDAP 对象设置一个限制选项 - LDAP.limitation
。 这是一个整数,指定搜索操作中可以返回的最大条目数。
// set the limitation for searching result set. In this example, only the first 1000 entries will return.
connection.limitation = 1000
PerfectLDAP 提供了 add() / modify() 和 delete(),用于同步和异步选项的属性操作。
函数 LDAP.add()
可以使用以下参数将属性添加到特定的 DN
异步 add() 和同步 add() 都共享上面的相同参数,请看示例
// try an add() synchronously.
do {
try connection.add(distinguishedName: "CN=judy,CN=User,DC=perfect,DC=com", attributes: ["mail":["judy@perfect.com", "judy@perfect.org"]])
}catch (let err) {
// failed for some reason
}
// try and add() asynchronously:
connection.add(distinguishedName: "CN=judy,CN=User,DC=perfect,DC=com", attributes: ["mail":["judy@perfect.com", "judy@perfect.org"]]) { err in
// if nothing wrong, err will be nil
}
函数 LDAP.modify()
可以使用以下参数从特定的 DN 修改属性
异步 modify() 和同步 modify() 都共享上面的相同参数,请看示例
// try and modify() synchronously.
do {
try connection.modify(distinguishedName: "CN=judy,CN=User,DC=perfect,DC=com", attributes: ["codePage":["437"]])
}catch (let err) {
// failed for some reason
}
// try and modify() asynchronously:
connection.modify(distinguishedName: "CN=judy,CN=User,DC=perfect,DC=com", attributes:["codePage":["437"]]) { err in
// if nothing wrong, err will be nil
}
示例:从组中添加和删除用户
// add user to group
do {
try connection.modify(distinguishedName: "CN=employee_group,CN=Group,DC=perfect,DC=com", attributes: ["member":["CN=judy,CN=User,DC=perfect,DC=com"]], method: LDAP_MOD_ADD | LDAP_MOD_BVALUES)
}catch (let err) {
// failed for some reason
}
// remove user from group
do {
try connection.modify(distinguishedName: "CN=employee_group,CN=Group,DC=perfect,DC=com", attributes: ["member":["CN=judy,CN=User,DC=perfect,DC=com"]], method: LDAP_MOD_DELETE | LDAP_MOD_BVALUES)
}catch (let err) {
// failed for some reason
}
函数 LDAP.delete()
可以使用一个参数从特定的 DN 删除属性
异步 delete() 和同步 delete() 都共享上面的相同参数,请看示例
// try an delete() synchronously.
do {
try connection.delete(distinguishedName: "CN=judy,CN=User,DC=perfect,DC=com")
}catch (let err) {
// failed for some reason
}
// try and delete() asynchronously:
connection.delete(distinguishedName: "CN=judy,CN=User,DC=perfect,DC=com") { err in
// if nothing wrong, err will be nil
}
有关 Perfect 项目的更多信息,请访问 perfect.org。