上次的kerberos效果不错,所以这次继续介绍安全领域中另一个重要的协议---ldap。
ldap全称是轻型目录访问协议(lightweight directory access protocol),它是一种建立在TCP/IP基础上的应用协议 ,因为ldap协议的特性,现在一般用于SSO单点登陆,成为安全认证中比较常见且重要的部分。上期kerberos解决了一个域中你自己的身份强认证,ldap做的就是在另一个角度,即可以通过节点方式存储用户身份来进行用户认证的工具。其实网上关于ldap的资料不算少,但大多刚理解起来也是一头雾水,这篇文章也是先从理解的角度 帮助你知道ldap究竟是做什么的,以及为什么我们会使用它,然后带出来一些ldap的基础知识。
值得一提的是本篇只叙述关于ldap的知识,关于其上层应用框架openldap或者apacheds,可能后期会拿一篇继续补。
首先提出一个问题,如果你的系统需要加身份认证的功能,你会怎么做?
当然,如果不是集群间强认证需求,你可能用不到kerberos。往往想到的是把用户的权限信息存储到后端数据库中,用户请求发送过来先从数据库中判断下权限认证,然后再继续进行下去。所以这部分权限信息要如何存储,如何制定权限信息规范,如何支持可扩展 让多个系统可以用一套权限系统。这都是我们要面对的问题。
那为什么是目录型数据库?
目录型数据库是一种从关系型数据库分离出来用于特制需求的数据库,它的一个重要特征是读的次数比写多很多。一个经典例子是电话本,我们可能经常查阅电话本,但每个人电话号码是很少更换的,所以它的查阅次数会比写入次数多很多,这就产生了需求我们可不可以用一个特制数据库去存储静态数据,而不适合快速大量修改的数据应用场景。一方面因为传统关系型数据库在大量修改的场景下,你不得不考虑一些分布式问题,针对CAP进行设计,我们用这种目录型特制化数据库就变得很轻量级,而且易部署易使用,这就很好理解为什么ldap会是lightweight的。
另一方面,目录型数据库可以避免事务的操作,权限这部分完全可以在数据库事务中分离出产生两阶段,而不需要通过一个事务进行控制,这样也会减少对主数据库的压力。
当业务量上来之后,我们的目的是简化并优化整个系统,无论是客户端还是服务端。这就要求在一些简单的场景下避免去使用用不到的复杂逻辑。再比如SQL是很强大的查询语言,但在查权限这种频繁简单的场景下也会显得冗余,如果我们使用目录型数据库,通过一种特制化的通信协议去满足这种简单的需求,客户端服务端也容易部署,是不是会解决问题呢?于是ldap就诞生了。
ldap的使用普及并不是因为它的功能有多么强大,而是它规范了一套各种服务都可以用的模板,让每个应用都可以轻松接入。
这部分我们从网络大框架介绍到ldap的节点设计:
图1 ldap传输过程
之前说过ldap是建立在tcp/ip层上的协议,当我们客户端请求通过应用层转发到传输层的过程通过调用client的api封装,并通过tcp/ip转发到directory server,调用后台的目录存储返回认证信息。图中展现的是一个ldap client和server以及存储介质在网络中的传输过程。
可见,ldap的设计其实是遵循了osi网络架构的规范,在其之上实现的网络协议,并基于一些基础协议模型比如x.500的规范建立起的一个新网络协议。那么ldap究竟规范了一个怎样的世界呢?
以下内容我会尽量中英文都有标注,概念较多有利于理解消化。
fig.2 一个ldap DIT的例子
ldap目录的组织方式是一种有层次有结构的树形结构DIT,其中条目(entry)由属性(attribute)的一个聚集组成,并每个entry都有一个唯一性的名字引用,即专有名称(DN)。
fig3. 在apacheds上看到的ldap一个节点的内容
由此可见,在ldap中信息以树的形式组织,在树状信息中的基本数据单元是条目,而每个条目由属性构成,属性中存储有属性值。
fig.4 entry与attribute定义
由于ldap中可能条目很多,我们改动的时候会很费力,所以ldap支持ldif(LDAP Data Interchange Format )有效管理这些条目。
ldap中的条目中定位是每个条目都有自己的DN,DN是该条目在整个树中的唯一名称标识,相当于文件系统中的绝对路径。
以下是概念总结(不限于ldap命名模型):
DIT : ldap的树结构,每个树上的节点叫entry
Entry: 条目, 每个条目就是一条记录,每个条目有自己的唯一可区别的名称(DN)。
DN (Distinguished Name):从特定节点到树的根的直接下级的节点的路径序列为DN,区别于其他节点的名字,相当于节点的绝对路径,由不同部分的rdn组成, 比如"uid=songtao.xu,ou=oa组,dc=example,dc=com”,一条记录的位置(唯一)
RDN(Relative Distinguished Name): 区别于同层节点的名字叫RDN,相对辨别名,类似于文件系统中的相对路径,它是与目录树结构无关的部分,如“uid=tom”或“cn= Thomas Johansson”
Attribute:每个节点属性都有相应的value,存放在ldif文件中
objectClass: 对象类(ObjectClass)是属性的集合,LDAP预想了很多人员组织机构中常见的对象,并将其封装成对象类。对象类是可以继承的,这样父类的必须属性也会被继承下来。
schema: 对象类、属性类型、语法分别约定了条目、属性、值。这些构成了模式(Schema),模式中的每一个元素都有唯一的OID编号
Cursor:游标,指向查找的目前节点,有向前插入和向后插入两种方式,通过此游标可以存/取/删除数据项
search scope:查询范围,默认有三种。baseObject:作用域仅限于由命名的条目;singleLevel:baseobject的直接子节点一层;wholeSubtree: baseObject及其所有子树集合
其他一些常见的规范:
dc: Domain Component 域名的部分,其格式是将完整的域名分成几部分,如域名为example.com变成dc=example,dc=com(一条记录的所属位置)
uid: User Id, 用户ID songtao.xu(一条记录的ID)
ou: Organization Unit, 组织单位,组织单位可以包含其他各种对象(包括其他组织单元),如“oa组”(一条记录的所属组织)
cn: Common Name, 公共名称,如“Thomas Johansson”(一条记录的名称)
sn: Surname 姓,如“许”
o: organization
值得一提的是ldap属性中有很多是定制化的比如entryuuid,oid等特殊标识了一个节点的信息,需要通过特定的协议规范去生成。关于一些string和url格式也要符合RFC规范。
如此一来,以上的命名规范形成属性存储在条目中,一系列的条目继承各不相同的objectclass扩展并整理在ldif文件中在后端存储,访问时通过条目的dn查询已定义好的属性得到想要的内容。
ldap定义了如何去获取或者修改条目的操作,基本上分三个类别:查找/修改/认证。在此之上我们可以直接用ldap命令(http://benjr.tw/27676)实现对条目的操作。关于安全模型,和mysql一样,ldap后端也支持各种安全模型(无认证模式/基本认证/sasl/ssl&tls)
最后,争议比较多的是ldap实现认证方式是否真的比http+mybatis+关系型数据库要好也值得讨论,我会在之后的系列有时间会详细分析下,针对于ldap协议其实不存在认证方式对比性,只能说ldap提供了一种轻量级,多扩展地认证实现方式,这种方式虽然可能有点过于定制化使得前期学习门槛或者维护过高,不过不得不说解决了实际中的后端认证问题并大量应用于生产环境中,现在成为安全认证中一种可选且不能忽视的一种方式。
参考:
1. Understanding LDAP Design and Implementation, June 2004, IBM
2. geeksforgeeks, https://www.geeksforgeeks.org/lightweight-directory-access-protocol-ldap/
3. https://www.cnblogs.com/wilburxu/p/9174353.html