LDAP应用技术简述


原文链接 http://www.daifusecure.com/articles/ldap2.php

一、             客户端访问工具;


a)        openldap命令行;


Openldap提供了在UNIX命令行下的访问工具集。包括ldapsearch,ldapadd,ldapmodify,ldappassword,ldapdelete等必要的工具。除了使用man获得使用帮助外,还可以在http://www.tldp.org/HOWTO/LDAP-HOWTO/,及http://www.csis.gvsu.edu/GeneralInfo/Oracle/network.920/a96579/,获得使用的支持。

例子:通过查询根上下文判断LDAP服务器是否正常工作:

$ ldapsearch -x -b "" -s base "objectclass=*" namingContexts

注:该命令查询该当前服务器上的命令上下文,通常就是rootdn的上下文记录。

-x    指该查询使用目录认证而不是使用SASL认证,;

-b “” 是查询的起点,即base,空指从根开始查询;

-s base 指查询范围。有三种选项,one指一层,包括兄弟条目;base指当前条目,sub,子孙记录。默认是sub.

“objectclass=*” 是过滤器,表示所有记录类型都加以选择;

namingContexts是约定的特殊属性,可以选择其他属性值进行查询。

$ ldapsearch -x -b "dc=daifu,dc=com" -s base "objectclass=organization" dn dc

注:

-b “dc=daifu,dc=com” 指查询的是“dc=daifu,dc=com”的条目,需要注意的是,slapd.conf中指定rootdn为“dc=daifu,dc=com”,并不等同于目录中已经具有真实的“dc=daifu,dc=com”条目。

"objectclass=organization" 指查询条件是organization类的。

“dc dc”指只需列出dn,dc两项属性。

 (ldapadd),ldapmodify的操作是基于LDIF文件的,所以必须先按规则生成LDIF文本文件,然后执行导入。要注意的是,新装的LDAP具备一个上下文,并不等于在目录中有相应的条目。如,OPENLDAP的slapd.conf中已经定义了一个根“dc=daifu,dc=com”,并不等于可以把新的条目添加到”dc=daifu,dc=com”,因此实际上并没有这一条目,必须先执行添加相应的条目,然后才可以添加后续条目。

其次,LDIF的格式文件非常严格,空间被认为是确定的字符,因此,需要特别注意每行后面不应带有空格。

b)        ldapbrowser;


    ldapbrowser是开源的LDAP浏览工具,并带有不太强的条目编辑功能。Ldapbrowser是纯JAVA的程序,可跨平台运行,在开源的程序中,是最优秀的一个。缺点是不能浏览服务器端的schema对象,从而限制了条目编辑(添加)的能力。

其次,由于JAVA对LDAP的访问方式在添加时,必须预先生成一个实现DirContext接口的类,因此,这也令订制型的添加操作在JAVA实现时相当困难。

 c)         ldapadministrator;


ldapadministrator是一个windows的收费程序,试用一个月。Ldapadministrator除了具备ldapbrowser的功能外,在条目编辑上的功能大为增强。

但从另一个角度看,LDAP总是涉及到大量的条目,当需要编辑的条目急速增加时,使用ldapadminstrator就不是轻松的事情,此时还是使用LDIF文件交    换为佳。

         d)        浏览器;


    根据rfc2255.txt的约定,可以使用URI定义LDAP查询,因此,理论上,只要浏览器内嵌支持,就可以作为LDAP客户端使用。   IE浏览器支持简单的LDAP查询。此时,IE把LDAP的URI看作是查询的对象。但是IE以及Exchange server非常狭隘地把LDAP看作是纯粹为EMAIL地址查询服务的,只能以“图形”的方式显示查到的邮件地址什么的。因此,IE准确地说,是对LDAP存储的邮件地址信息的查询工具。

URI查询语法是:

ldapurl    = scheme "://" [hostport] ["/"                   [dn ["?" [attributes] ["?" [scope]                    ["?" [filter] ["?" extensions]]]]]]      scheme     = "ldap"       attributes = attrdesc *("," attrdesc)       scope      = "base" / "one" / "sub"       dn         = distinguishedName from Section 3 of [1]       hostport   = hostport from Section 5 of RFC 1738 [5]       attrdesc   = AttributeDescription from Section 4.1.5 of [2]       filter     = filter from Section 4 of [4]       extensions = extension *("," extension)       extension  = ["!"] extype ["=" exvalue]       extype     = token / xtoken       exvalue    = LDAPString from section 4.1.2 of [2]       token      = oid from section 4.1 of [3]       xtoken     = ("X-" / "x-") token 


例:


类似

#ldapsearch ?Cx ?Ch 192.168.0.2 ?Cp 389 -b “dc=daifu,dc=com” ?Cb “sub” “objectclass=qmailuser”

的URI查询是:

ldap://192.168.0.2:389/dc=daifu,dc=com??sub?objectclass=qmailuser?

       浏览器并不是完全的LDAP客户工具。

e)        ldapexplorer(PHP)


一个用PHP写的LDAP处理工具 


 


二、             编写LDAP访问程序;


a)        JAVA


                        i.              JNDI(JAVA 命名及目录接口)


JNDI是JAVA为命名及目录服务访问制定的基础接口标准,用于访问包括DNS,NIS,LDAP,文件系统等任何以树目录形式存在目标对象,并且基本上可保持访问方式的一致(意味着代码共用)。对于不同的目录类型,JNDI通过使用不同的目录驱动,以保证访问方式的一致。

以下连接可获得较详细的讲解和例程:http://java.sun.com/products/jndi/tutorial/

经常使用的LDAP驱动有JDK自带的LDAP provider,还有IBM的PROVIDER,以及NOVEL的JNDI产品。需要提醒的是,JNDI中最重要的概念是上下文context,即定位从那一个入口开始操作,相当于openldap命令行中的-D开关的作用。其他的操作都是该上下文对象的调用。

LDAP通过JNDI添加条目是基于DirContext类的操作。由于JAVA只能以对象方式向LDAP添加条目,因此,必须首先具备实现DirContext接口的类,或使用DirContext(扩充了context接口)代替context,然后才能通过ctx.bind()的方法把该类添加到目录中。

JAVA-LDAP-ADD的操作可以有三种方式:

Context.bind()或ctx.createSubcontext("name");

或DirContext.bind(“dn”,attrs,object)的方式。对于没有预先编写实现DirContext接口的类对象的添加,这是唯一的办法。

                       ii.              JLDAP

JLDAP是由novel开发的,原是针对Novel的NDS目录设计的JAVA访问工具。NOVEL的NDS和网景(NETSCAPE)的目录是工具界最早的目录产品。JLDAP并非JNDI的服务供应者,而是同一抽象层次下的访问工具集。与JNDI-LDAP相比,JLDAP更接近于类关系数据库的访问方式。

NDS是遵守LDAP协议的并进行了扩展的类MAD产品。而NOVEL也已把JLDAP捐献给了OPENLDAP开源项目,可以世界范围内自由使用。与JNDI相比,JLDAP无须继承DirContext才能实现添加,也无需预先生成添加的类,可以象普通数据访问那样,生成连接,然后使用::add方法添加。这样,添加的灵活性要强于JNDI。

但由于JLDAP目前是访问NDS,因此,它不具备JNDI完全面向对象存储的能力,对于高级的LDAP应用,JLDAP不是合适的选择。

例:

import com.novell.ldap.*;

public class AddEntry

{

    public static void main( String[] args )

    {

        if (args.length != 4) {

            System.err.println("Usage:   java AddEntry <host name> <login dn>"

                                                + " <password> <container>");

            System.err.println("Example: java AddEntry Acme.com"

                        + " /"cn=admin,o=Acme/" secret /"ou=Sales,o=Acme/"");

            System.exit(1);

        }

              

        int ldapPort = LDAPConnection.DEFAULT_PORT;

        int ldapVersion  = LDAPConnection.LDAP_V3;

        String ldapHost       = args[0];

        String loginDN        = args[1];

        String password       = args[2];

        String containerName  = args[3];

        LDAPConnection lc = new LDAPConnection();

        LDAPAttribute  attribute = null;

        LDAPAttributeSet attributeSet = new LDAPAttributeSet();

        /* To Add an entry to the directory,

         *   -- Create the attributes of the entry and add them to an attribute set

         *   -- Specify the DN of the entry to be created

         *   -- Create an LDAPEntry object with the DN and the attribute set

         *   -- Call the LDAPConnection add method to add it to the directory

         */         

        String objectclass_values[] = { "inetOrgPerson" };

        attribute = new LDAPAttribute( "objectclass", objectclass_values );

        attributeSet.add( attribute );    

        String cn_values[] = { "James Smith", "Jim Smith", "Jimmy Smith" };

        attribute = new LDAPAttribute( "cn", cn_values );

        attributeSet.add( attribute );

        String givenname_values[] = { "James", "Jim", "Jimmy" };

        attribute = new LDAPAttribute( "givenname", givenname_values );

        attributeSet.add( attribute );

        attributeSet.add( new LDAPAttribute( "sn", "Smith" ) );

        attributeSet.add( new LDAPAttribute( "telephonenumber",

                                                     "1 801 555 1212" ) );

        attributeSet.add( new LDAPAttribute( "mail", "JSmith@Acme.com" ) );

        String  dn  = "cn=JSmith," + containerName;    

        LDAPEntry newEntry = new LDAPEntry( dn, attributeSet );

        try {

            // connect to the server

            lc.connect( ldapHost, ldapPort );

            // authenticate to the server

            lc.bind( ldapVersion, loginDN, password );

            lc.add( newEntry );

            System.out.println( "/nAdded object: " + dn + " successfully." );

            // disconnect with the server

            lc.disconnect();

        }

        catch( LDAPException e ) {

            System.out.println( "Error:  " + e.toString());

        }                                 

        System.exit(0);

    }

}

                      iii.              JdbcLDAP

JDBCLDAP是OcterString提供的,能过类SQL实现LDAP访问的工具。JDBCLDAP是针对大量熟悉SQL而对LDAP欠缺了解的程序员而设计的,可以完成简单的LDAP查询、插入、更新、删除这样的工作。

JdbcLDAP使用LDAP-JDBC驱动访问“LDAP数据库”:

Class.forName("com.octetstring.jdbcLdap.sql.JdbcLdapDriver");


连接时使用各个DN的具体权限建立连接:


String ldapConnectString =   "jdbc:ldap://localhost:389/dc=examples,dc=com?SEARCH_SCOPE:=subTreeScope";
java.sql.Connection con;
con = DriverManager.getConnection(ldapConnectString,"cn=Admin","manager");


连接字符串遵从标准的LDAP-URL格式,(RFC2255)。

SQL操作时,将每一个目录ENTRY看作是一个统一表的一行,然后把属性看作列,如:

String SQL = "INSERT INTO cn,ou (objectClass,objectClass,objectClass,ou,sn,cn) " +
  "VALUES (top,person,organizationalPerson,Product Development,Boorshtein," +
  "Marc Boorshtein)";
Statement insert = con.createStatement();
int count = insert.executeUpdate(SQL);
if (count < 1) {
  System.out.println("Insert Failed");
} else {
  System.out.println("Insert Succeeded");
}


    Ou,sn,cn是新条目的入口标识。

        Jdbc-LDAP不可以完成串行化binding,因此,只适宜对已有的LDAP进行临时访问(如程序员不熟悉,或保持旧程序,仅修改必要的连接项),不宜把整个项目建筑在JDBC-LDAP上。实际上,LDAP本身就是一种访问的前端协议,硬要把SQL再作为前端使用,是完全没有必要的。

 b)        C语言:


包括openldap,netscape(sun),mozilla, novell,ibm等,都提供了LDAP的C SDK和接口函数。作为RFC标准的LDAP结构,struct LDAP是定义为对用户隐藏的,并在各个实现函数中各自定义,以便适应不同的LDAP访问。

#typedef struct ldap LDAP在ldap.h中定义;在2.0版以后,struct LDAP改为隐藏,只能能过函数ldap_set_option 和ldap_get_option访问。(draft-ldapext-ldap-c-api-xx.txt)

使用时:

如:

LDAP *Ld=null;  //声明并初始化LDAP类型的变量指针 *ld;

Ld       =ldap_init( ldaphost, ldapport );  //获取LDAP的会话;

 


获得会话后,调用ldap_simple_bind_s获得访问LDAP的权限,然后就可以调用不同的函数,如

ldap_search_ext( ld, base, scope, filter, attrs, attrsonly,

              sctrls, cctrls, timeout, sizelimit, &msgid );

即可完成相关的操作。

即步骤为:1。获得会话;2。绑定对象;3。执行操作。

 


连接例子:

#include <stdio.h>


#include "ldap.h"


/* Adjust these setting for your own LDAP server */


#define HOSTNAME "localhost"


#define PORT_NUMBERLDAP_PORT


#define FIND_DN "uid=bjensen, ou=People, o=Airius.com"


int


main( int argc, char **argv )


{


LDAP*ld;


LDAPMessage*result, *e;


BerElement*ber;


char*a;


char**vals;


int i, rc;


/* Get a handle to an LDAP connection. */


if ( (ld = ldap_init( HOSTNAME, PORT_NUMBER )) == NULL ) {


perror( "ldap_init" );


return( 1 );


}


/* Bind anonymously to the LDAP server. */


rc = ldap_simple_bind_s( ld, NULL, NULL );


if ( rc != LDAP_SUCCESS ) {


fprintf(stderr, "ldap_simple_bind_s: %s/n", ldap_err2string(rc));


return( 1 );


}


/* Search for the entry. */


if ( ( rc = ldap_search_ext_s( ld, FIND_DN, LDAP_SCOPE_BASE,


"(objectclass=*)", NULL, 0, NULL, NULL, LDAP_NO_LIMIT,


LDAP_NO_LIMIT, &result ) ) != LDAP_SUCCESS ) {


fprintf(stderr, "ldap_search_ext_s: %s/n", ldap_err2string(rc));


return( 1 );


}


/* Since we are doing a base search, there should be only


one matching entry. */


e = ldap_first_entry( ld, result );


if ( e != NULL ) {


printf( "/nFound %s:/n/n", FIND_DN );


/* Iterate through each attribute in the entry. */


for ( a = ldap_first_attribute( ld, e, &ber );


a != NULL; a = ldap_next_attribute( ld, e, ber ) ) {


/* For each attribute, print the attribute name and values. */


if ((vals = ldap_get_values( ld, e, a)) != NULL ) {


for ( i = 0; vals[i] != NULL; i++ ) {


printf( "%s: %s/n", a, vals[i] );


}


ldap_value_free( vals );


}


ldap_memfree( a );


}


if ( ber != NULL ) {


ber_free( ber, 0 );


}


}


ldap_msgfree( result );


ldap_unbind( ld );


return( 0 );


}


                        i.              Novell函数库:


Novel提供了基于普通LDAP函数库的扩展,主要包括两个部分:针对Novel eDirectory服务器产品的扩展,其次是对如ldapsearch等常用函数的扩展。详情可从:http://developer.novell.com/ndk/qstart/opensource.htm#ldapc  获得帮助;

                      ii.              Netscape函数库;


Netscape一度是企业级目录服务提供者,许多LDAP的C例子,实际上都是基于Netscape服务器的。但在Netscape被收购后,其目录服务成了iPlanet和SUN eDirectory产品的一部分,出于支持JAVA和iplanet产品的缘故,SUN对该产品和相关库的支持远不够积极,特别是对linux的支持不够充分,估计也与保护solaris产品有关。

                    iii.              Mozilla函数库:


Mozilla可以看作是Netscape的另一个分支。准确地说,Netscape本来就是源于Mozilla。Mozilla是也是一个开源的项目,提供完整的C-SDK,缺点是对linux的支持不够充分。

c)         Perl接口


   Perl 的NET::LDAP模块中包括有完整的LDAP目录访问函数,只要安装NET::LDAP就可以完成正常的LDAP目录访问;但在安装NET::LDAP模块前,必须先安装Convert::ASN1模块,该模块可以从CPAN下载。

   例:

#!/usr/bin/perl

 


use warnings;

use strict;

 


use Net::LDAP;

use Net::LDAP::Util qw(ldap_error_text);

 


my $server = "localhost";

my $ldap = new Net::LDAP($server) ||

   die("failed to connect to server.$!/n");

 


my $mesg = $ldap->bind("cn=Manager,dc=daifu,dc=com", password => "secret");

 


die ("bind failed with ",ldap_error_text($mesg->code()),"/n")

   if $mesg->code();

 


$mesg = $ldap->search(base => "dc=daifu,dc=com", scope => "sub",

   filter => "sn=*",);

 


die ("search failed with ",ldap_error_text($mesg->code()),"/n")

   if $mesg->code();

 


print "Count is ",$mesg->count(),"/n";

 


while (my $entry = $mesg->shift_entry()) {

   print "dn:",$entry->dn(),"/n";

   for my $attr($entry->attributes()) {

      for my $val($entry->get_value($attr)) {

         print "$attr:$val/n";

      }

   }

 


   print "/n";

}

操作过程实际上与C和JAVA是一样的。

d)        PHP


        PHP带有自已的LDAP处理接口,就程序方式而言,却是最方便简洁地处理某一 entry记录的工具。这也是PHP的一个优点——对于常用的


工具有完整的函数库可供调用。详情参考:用PHP访问LDAP

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 


 
本文引用通告地址(TrackBack Ping URL)为:

http://blog.ccw.com.cn/trackback.jsp?postID=7826
 
 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值