实现 LDAP 验证登录

在我写的 KPI 工具中 需要做 MSAD 账号集成,网上找到的资料有点混乱,我把最重要的信息整理在这里,保证一看就懂。 :)

LDAP 协议是 Lightweight Directory Access Protocol 的简称。它是一个通用协议,并不依赖于特定平台。

MSAD(Microsoft Active Directory) 是微软服务器操作系统中基于 LDAP 协议的具体实现。

LDAP 是一个类似于 Unix 目录树的结构。对于 Unix 目录树来说,项目可以是文件或者目录,文件项不能再包含下级目录。而 LDAP 则没有这个限制。

先说几个经常出现的缩写词的含义:

  • DN = Distinguished Name 唯一名称
  • CN = Common Name 通用名
  • UID = User ID
  • OU = Organization Unit 组织单元
  • DC = Domain Componen
  • O = Organization 组织
  • C = Country 国家名

LDAP 中最基本的信息单元是 Entry ,用 DN 可以标识一个条目的位置,从名称可以看出,DN 是唯一的。我们需要用 DN 指定一个确定的条目。

我司的 LDAP 服务器根目录的 DN 是: DC=baina, DC=com ,这句话就和 Unix 的根目录 / 的含义一致。LDAP 协议规定还可以用 O 和 C 来指定一个 DN。例如根目录的 DN 也可以写成: O="baina, Inc.",C=CN 或者 O=baina.com。但 MSAD 限制必须使用 DC 的这种写法,所以我只记住第一种就好。

在我司的 LDAP 服务中,我的信息也是一个 Entry,我的 DN 是这样的:

1CN="Rong Zeng", OU=Game, OU=Wuhan, OU=FTE, OU=Mobile, DC=baina, DC=com

很好懂吧?通过 CN 和 OU 就把我进行了分类。可以看出我是 Wuhan 分公司、Game 部门、移动部门、正式员工。

换成 Unix 目录树的表达方式,可能是这样的: /wuhan/game/fte/rong zeng ,但是 Unix 目录树无法处理并列关系。wuhan、game、fte 这三个 OU 并不是层级关系,而是并列关系。

一个 Entry 可以包含多个 OU,也可以包含多个 CN。可以用 UID 代替 CN 来表示名字。我司的服务器并没有使用 UID,就不研究了。

一个 Entry 可以包含多个 Attribute 。每个 Entry 中含有哪些 Attribute 是由 object class 决定的。object class 和 OU 不同,OU 纯粹用于分类,而 object class 则规定了每个条目必须或可选的属性。每个 Entry 可以属于多个 object class。

常用的 object class 有下面几个:

  • top 所有 class 的基类
  • person
  • organizationalPerson 继承 person
  • organization
  • organizationalRole
  • organizationalUnit

例如 person 要求 CN 和 SN(Surename) 不能为空,并允许 givenname、telephonenumber 等属性为可选。organizationalPerson 则增加了更多可选的属性。

当然,自定义 object class 也是允许的。

最重要的概念都介绍完了,下面就直入主题。

我的网站使用 Flask 写成,因此需要一个 Python 的 LDAP 支持库,有两个库被广为使用:

  • python-ldap python-ldap provides an object-oriented API to access LDAP directory servers from Python programs. Mainly it wraps the OpenLDAP 2.x libs for that purpose. Additionally the package contains modules for other LDAP-related stuff.
  • ldap3 A strictly RFC 4510 conforming LDAP V3 pure Python client library. The same codebase works with Python 2, Python 3, PyPy and PyPy3

python-ldap 主要是 C 实现的,仅支持 Python2 。ldap3 则为纯 Python 实现,不仅支持 Python3, 还支持 PyPy。

下面是一段使用 ladap3 在我司的 MSAD 服务器上实现鉴权的代码:

 1import ldap3
 2
 3def authorize(host=None, port=None, user=None, password=None):
 4    if not host:
 5        return False, 'no host'
 6    server = ldap3.Server(host, port, get_info=ldap3.ALL)
 7    conn = None
 8    auto_bind = False
 9    try:
10        if user:
11            user = 'baina\\%s'%user
12            if password:
13                auto_bind = True
14        conn = ldap3.Connection(server, user=user, password=password, auto_bind=auto_bind, authentication=ldap3.NTLM)
15        if not auto_bind:
16            succ = conn.bind()
17        else:
18            succ = True
19        msg = conn.result
20        conn.unbind()
21        return succ, msg
22    except Exception as e:
23        if conn:
24            conn.unbind()
25        return False, e

主要的参考内容如下:

0 文件

(全文完)