`
Jack Wu
  • 浏览: 892897 次
  • 来自: ...
社区版块
存档分类
最新评论

JNDI 连接Windows Active Directory 教程

阅读更多

這編主要是描述 Java JNDI 連 Windows Active Directory 的教程.
包括認證, 新增用戶, 修改密碼 及 取得用戶資料.


開始教程:
1. 建立 IIS SSL
2. 將 CA Certificate 加入至 jre keystore 裡
3. JNDI 連 AD

1. 建立 IIS SSL:
Install Windows 2003 Server:

Install AD:
Start -> Run -> dcpromote
domain name : joeyta-DOT-local
NT domain name : joeytaserver
即 Fully Qualified Domain Name (FQDN) 為 joeytaserver.joeyta-DOT-local

先安裝 IIS , 再安裝 CA.

Install IIS:
Start -> Programs -> Administrative Tools -> Configure Your Server Wizard
->> Next -> Next -> Application server (IIS, ASP.NET) -> Next

進入 http://joeyserver.joeyta.local/postinfo.html 表示安裝成功.

Install CA:
Start -> Settings -> Control Panel -> Add or Remove Programs
->> Add/Remove Windows Components
選擇 Certificate Services -> Next
選擇 Enterprise root CA -> Next
Common name for this CA: testca -> Next

進入 http://joeyserver.joeyta.local/CertSrv 表示安裝成功.


Generating a Certificate Signing Request:
Start -> Programs -> Administrative Tools -> Internet Information Services (IIS) Manager
->> Internet Information Services -> (local computer) -> Web Sites
-> > 右鍵點選 Default Web Site -> Properties
選擇 "Directory Security" -> Server Certificate
->> Create a new certificate -> Prepare the request now, but send it later
一直按 Next , 需要注意的是 Common name 必須為 joeyserver.joeyta.local, 這是給使用者連 ssl 的 website.
最後產生 certificate request file , 預設為 c:\certreq.txt


Request a certificate on CA:
進入 http://joeyserver.joeyta.local/CertSrv
按 Request a certificate -> advanced certificate request
-> Submit a certificate request by using a base-64-encoded CMC or PKCS#10 file, or submit a renewal request by using a base-64-encoded PKCS#7 file
使用 notepad 打開 c:\certreq.txt , copy c:\certreq.txt 內容貼至 Saved Request:
Certificate Template 選擇 Web Server, 按 Submit
然後點選 Download certificate , 將 certnew.cer 儲存至 c:\certnew.cer


Installing a Certificate:
Start -> Programs -> Administrative Tools -> Internet Information Services (IIS) Manager
->> Internet Information Services -> (local computer) -> Web Sites
-> > 右鍵點選 Default Web Site -> Properties
選擇 "Directory Security" -> Server Certificate
->> Process the pending request and install the certificate -> Next
Path and file name: c:\certnew.cer -> Next
SSL port this web site should use: 443 -> Next -> Next -> Finish


2. 將 CA Certificate 加入至 jre keystore 裡:
進入 http://joeyserver.joeyta.local/CertSrv
點選 Download a CA certificate, certificate chain, or CRL
點選 Download CA certificate , 然後下載並改名為 c:\testca_cert.cer

然後執行 command:
c:\temp>keytool -import -alias testca_cert -file "/testca_cert.cer" -keystore "/jdk1.5.0_09/jre/lib/security/cacerts" -storepass "changeit"

出現 Trusted this certificate? 按 "y" 即新增成功.


3. JNDI 連 AD:

/***************************** LDAPFastBind.java *****************/
package test.ldap;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.Hashtable;

import javax.naming.AuthenticationException;
import javax.naming.Context;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.directory.Attribute;
import javax.naming.directory.Attributes;
import javax.naming.directory.BasicAttribute;
import javax.naming.directory.BasicAttributes;
import javax.naming.directory.DirContext;
import javax.naming.directory.ModificationItem;
import javax.naming.directory.SearchControls;
import javax.naming.directory.SearchResult;
import javax.naming.ldap.Control;
import javax.naming.ldap.InitialLdapContext;
import javax.naming.ldap.LdapContext;
import javax.naming.ldap.StartTlsRequest;
import javax.naming.ldap.StartTlsResponse;

class FastBindConnectionControl implements Control {
    public byte[] getEncodedValue() {
        return null;
    }

    public String getID() {
        return "1.2.840.113556.1.4.1781";
    }

    public boolean isCritical() {
        return true;
    }
}

public class LDAPFastBind {
    public Hashtable env = null;

    public LdapContext ctx = null;

    public Control[] connCtls = null;

    public LDAPFastBind(String ldapurl) {
        env = new Hashtable();
        env.put(Context.INITIAL_CONTEXT_FACTORY,
                "com.sun.jndi.ldap.LdapCtxFactory");
        env.put(Context.SECURITY_AUTHENTICATION, "simple");
        env.put(Context.PROVIDER_URL, ldapurl);
        
        env.put(Context.SECURITY_PROTOCOL,"ssl");

        String keystore = "/jdk1.5.0_09/jre/lib/security/cacerts";
        System.setProperty("javax.net.ssl.trustStore",keystore);
        
        connCtls = new Control[] { new FastBindConnectionControl() };

        // first time we initialize the context, no credentials are supplied
        // therefore it is an anonymous bind.

        try {
            ctx = new InitialLdapContext(env, connCtls);

        } catch (NamingException e) {
            System.out.println("Naming exception " + e);
        }
    }

    public boolean Authenticate(String username, String password) {
        try {
            ctx.addToEnvironment(Context.SECURITY_PRINCIPAL, username);
            ctx.addToEnvironment(Context.SECURITY_CREDENTIALS, password);
            ctx.reconnect(connCtls);
            System.out.println(username + " is authenticated");
            return true;
        }

        catch (AuthenticationException e) {
            System.out.println(username + " is not authenticated");
            System.out.println(e);
            return false;
        } catch (NamingException e) {
            System.out.println(username + " is not authenticated");
            System.out.println(e);
            return false;
        }
    }

    public void finito() {
        try {
            ctx.close();
            System.out.println("Context is closed");
        } catch (NamingException e) {
            System.out.println("Context close failure " + e);
        }
    }

    public void printUserAccountControl() {
        try {

            // Create the search controls
            SearchControls searchCtls = new SearchControls();

            // Specify the search scope
            searchCtls.setSearchScope(SearchControls.SUBTREE_SCOPE);

            // specify the LDAP search filter
            //String searchFilter = "(&(objectClass=user)(CN=test))";
            //String searchFilter = "(&(objectClass=group))";
            String searchFilter = "(&(objectClass=user)(CN=peter lee))";

            // Specify the Base for the search
            String searchBase = "DC=joeyta,DC=local";

            // initialize counter to total the group members
            int totalResults = 0;

            // Specify the attributes to return
            String returnedAtts[] = { "givenName", "mail" };
            searchCtls.setReturningAttributes(returnedAtts);

            // Search for objects using the filter
            NamingEnumeration answer = ctx.search(searchBase, searchFilter,
                    searchCtls);

            // Loop through the search results
            while (answer.hasMoreElements()) {
                SearchResult sr = (SearchResult) answer.next();

                System.out.println(">>>" + sr.getName());

                // Print out the groups

                Attributes attrs = sr.getAttributes();
                if (attrs != null) {

                    try {
                        for (NamingEnumeration ae = attrs.getAll(); ae
                                .hasMore();) {
                            Attribute attr = (Attribute) ae.next();
                            System.out.println("Attribute: " + attr.getID());
                            for (NamingEnumeration e = attr.getAll(); e
                                    .hasMore(); totalResults++) {

                                System.out.println(" " + totalResults + ". "
                                        + e.next());
                            }

                        }

                    } catch (NamingException e) {
                        System.err.println("Problem listing membership: " + e);
                    }

                }
            }

            System.out.println("Total attrs: " + totalResults);

        }

        catch (NamingException e) {
            System.err.println("Problem searching directory: " + e);
        }

    }
    
    public boolean adminChangePassword(String sUserName, String sNewPassword){
        try {
        
            //set password is a ldap modfy operation
            ModificationItem[] mods = new ModificationItem[1];

            //Replace the "unicdodePwd" attribute with a new value
            //Password must be both Unicode and a quoted string
            String newQuotedPassword = "\"" + sNewPassword + "\"";
            byte[] newUnicodePassword = newQuotedPassword.getBytes("UTF-16LE");

            mods[0] = new ModificationItem(DirContext.REPLACE_ATTRIBUTE, new BasicAttribute("unicodePwd", newUnicodePassword));

            // Perform the update
            ctx.modifyAttributes(sUserName, mods);
        
            System.out.println("Reset Password for: " + sUserName);    
            
            return true;
        }
        catch (NamingException e) {
            System.out.println("Problem resetting password: " + e);
        }
        catch (UnsupportedEncodingException e) {
            System.out.println("Problem encoding password: " + e);
        }
        return false;
    }
    
    public boolean userChangePassword(String sUserName, String sOldPassword, String sNewPassword){
        try {
            //StartTlsResponse tls = (StartTlsResponse)ctx.extendedOperation(new StartTlsRequest());
            //tls.negotiate();
            
            //change password is a single ldap modify operation
            //that deletes the old password and adds the new password
            ModificationItem[] mods = new ModificationItem[2];

            //Firstly delete the "unicdodePwd" attribute, using the old password
            //Then add the new password,Passwords must be both Unicode and a quoted string
            String oldQuotedPassword = "\"" + sOldPassword + "\"";
            byte[] oldUnicodePassword = oldQuotedPassword.getBytes("UTF-16LE");
            String newQuotedPassword = "\"" + sNewPassword + "\"";
            byte[] newUnicodePassword = newQuotedPassword.getBytes("UTF-16LE");
        
            mods[0] = new ModificationItem(DirContext.REMOVE_ATTRIBUTE, new BasicAttribute("unicodePwd", oldUnicodePassword));
            mods[1] = new ModificationItem(DirContext.ADD_ATTRIBUTE, new BasicAttribute("unicodePwd", newUnicodePassword));

            // Perform the update
            ctx.modifyAttributes(sUserName, mods);
        
            System.out.println("Changed Password for: " + sUserName);    
            //tls.close();
            return true;

        }
        catch (NamingException e) {
            System.err.println("Problem changing password: " + e);
        }
        catch (UnsupportedEncodingException e) {
            System.err.println("Problem encoding password: " + e);
        } catch ( Exception e){
            System.err.println("Problem: " + e);            
        }
        return false;
    }
    
    public boolean createNewUser(String sGroupName, String sUserName){
        try {
            // Create attributes to be associated with the new user
            Attributes attrs = new BasicAttributes(true);
            
            //These are the mandatory attributes for a user object
            //Note that Win2K3 will automagically create a random
            //samAccountName if it is not present. (Win2K does not)
            attrs.put("objectClass","user");
            attrs.put("sAMAccountName","AlanT");
            attrs.put("cn","Alan Tang");

            //These are some optional (but useful) attributes
            attrs.put("givenName","Alan");
            attrs.put("sn","Tang");
            attrs.put("displayName","Alan Tang");
            attrs.put("description","Engineer");
            attrs.put("userPrincipalName","alan-AT-joeyta.local");
            attrs.put("mail","alang-AT-mail.joeyta-DOT-local");
            attrs.put("telephoneNumber","123 456 789");
            
            //some useful constants from lmaccess.h
            int UF_ACCOUNTDISABLE = 0x0002;
            int UF_PASSWD_NOTREQD = 0x0020;
            int UF_PASSWD_CANT_CHANGE = 0x0040;
            int UF_NORMAL_ACCOUNT = 0x0200;
            int UF_DONT_EXPIRE_PASSWD = 0x10000;
            int UF_PASSWORD_EXPIRED = 0x800000;
        
            //Note that you need to create the user object before you can
            //set the password. Therefore as the user is created with no
            //password, user AccountControl must be set to the following
            //otherwise the Win2K3 password filter will return error 53
            //unwilling to perform.

            attrs.put("userAccountControl",Integer.toString(UF_NORMAL_ACCOUNT + UF_PASSWD_NOTREQD + UF_PASSWORD_EXPIRED+ UF_ACCOUNTDISABLE));    
        
            // Create the context
            Context result = ctx.createSubcontext(sUserName, attrs);
            System.out.println("Created disabled account for: " + sUserName);
            
            //now that we've created the user object, we can set the
            //password and change the userAccountControl
            //and because password can only be set using SSL/TLS
            //lets use StartTLS

            //StartTlsResponse tls = (StartTlsResponse)ctx.extendedOperation(new StartTlsRequest());
            //tls.negotiate();
        
            //set password is a ldap modfy operation
            //and we'll update the userAccountControl
            //enabling the acount and force the user to update ther password
            //the first time they login
            ModificationItem[] mods = new ModificationItem[2];
        
            //Replace the "unicdodePwd" attribute with a new value
            //Password must be both Unicode and a quoted string
            String newQuotedPassword = "\"P-AT-ssw0rd\"";
            byte[] newUnicodePassword = newQuotedPassword.getBytes("UTF-16LE");

            mods[0] = new ModificationItem(DirContext.REPLACE_ATTRIBUTE, new BasicAttribute("unicodePwd", newUnicodePassword));
            mods[1] = new ModificationItem(DirContext.REPLACE_ATTRIBUTE, new BasicAttribute("userAccountControl",Integer.toString(UF_NORMAL_ACCOUNT + UF_PASSWORD_EXPIRED)));
        
            // Perform the update
            ctx.modifyAttributes(sUserName, mods);
            System.out.println("Set password & updated userccountControl");


            //now add the user to a group.

                try    {
                    ModificationItem member[] = new ModificationItem[1];
                    member[0]= new ModificationItem(DirContext.ADD_ATTRIBUTE, new BasicAttribute("member", sUserName));
                
                    ctx.modifyAttributes(sGroupName,member);
                    System.out.println("Added user to group: " + sGroupName);

                }
                catch (NamingException e) {
                     System.err.println("Problem adding user to group: " + e);
                }
            //Could have put tls.close()  prior to the group modification
            //but it seems to screw up the connection  or context ?
            //tls.close();
        
            System.out.println("Successfully created User: " + sUserName);
            return true;
            
        }
        catch (NamingException e) {
            System.err.println("Problem creating object: " + e);
        }
    
        catch (IOException e) {
            System.err.println("Problem creating object: " + e);            
        }
        return false;
    }

    public boolean addUserToGroup(LdapContext ctx, String userDN, String groupDN) {
        try{
            ModificationItem[] mods = new ModificationItem[1];
            mods[0] = new ModificationItem(DirContext.ADD_ATTRIBUTE, new BasicAttribute("member", userDN));
            ctx.modifyAttributes(groupDN, mods);
            System.out.println("Added user " + userDN + " to group " + groupDN);
            return true;
        } catch (NamingException ne){
            System.err.println("Problem add user to group: " + ne);
        }
        return false;
    }

    public boolean removeUserFromGroup(LdapContext ctx, String userDN, String groupDN) {
        try{
            ModificationItem[] mods = new ModificationItem[1];
            mods[0] = new ModificationItem(DirContext.REMOVE_ATTRIBUTE, new BasicAttribute("member", userDN));
            ctx.modifyAttributes(groupDN, mods);
            System.out.println("Remove user " + userDN + " from group " + groupDN);
            return true;
        } catch (NamingException ne){
            System.err.println("Problem remove user from group: " + ne);
        }        
        return false;
    }    
    
}
/***************************** LDAPFastBind.java *****************/




/***************************** LDAPClient.java *****************/
package test.ldap;

class LDAPClient {
    public static void main(String[] args) {
        // Could also use ldaps over port 636 to protect the communication to
        // the
        // Active Directory domain controller. Would also need to add
        // env.put(Context.SECURITY_PROTOCOL,"ssl") to the "server" code
        //String ldapurl = "ldap://joeyserver.joeyta.local:389";
        String ldapurl = "ldap://joeyserver.joeyta.local:636";

        LDAPFastBind ctx = new LDAPFastBind(ldapurl);    
        
        String sAdminUserName = "CN=Administrator,CN=Users,DC=joeyta,DC=local";
        String sAdminPassword = "I@mRoot";        
        
//        String sUserName = "CN=peter lee,CN=Users,DC=joeyta,DC=local";
        String sUserName = "joeyta\\peter";        
//        String sUserName = "peter@joeyta.local";
        
        String sOldPassword = "P@ssw0rd";
        String sNewPassword = "P@$w0rd";
        
        String sNewUserName = "CN=Alan Tang,CN=Users,DC=joyeta,DC=local";
        String sNewGroupName = "CN=test,CN=Users,DC=joeyta,DC=local";        
        
        boolean IsAuthenticated = ctx.Authenticate(sAdminUserName, sAdminPassword);        
//        boolean IsAuthenticated = ctx.Authenticate(sUserName, sOldPassword);
                
        ctx.printUserAccountControl();
        
        ctx.createNewUser(sNewGroupName, sNewUserName);
        
        //boolean IsAdminSuccessChangePWD = ctx.adminChangePassword(sUserName,sNewPassword);
        //boolean IsUserSuccessChangePWD = ctx.userChangePassword(sUserName,sOldPassword,sNewPassword);
        
        ctx.finito();

    }
}
/***************************** LDAPClient.java *****************/
 连接来源:http://www.sawin.cn/doc/SP/Java/TheEdge202.htm
分享到:
评论

相关推荐

    java通过LDAP验证Active Directory服务

    为了通过Java实现对Active Directory的验证,我们可以使用JNDI API(Java Naming and Directory Interface),它提供了与不同目录服务(如LDAP)交互的接口。 下面是一段示例代码,展示了如何使用Java进行验证: `...

    Tomcat通过JNDI方式连接SqlServer数据库

    JNDI(Java Naming and Directory Interface)是一种Java API,用于访问命名和目录服务。在Tomcat中,我们可以使用JNDI来连接数据库。 首先,我们需要配置Tomcat来连接SqlServer数据库。我们可以在Tomcat的管理界面...

    java通过LDAP验证ActiveDirectory服务.pdf

    Active Directory是Microsoft Windows操作系统中的核心组件,用于存储和管理网络对象(如用户、计算机、组、打印机等)的目录服务。它提供了一个集中式的架构,使得管理员可以方便地控制网络资源和用户访问权限。AD...

    目录服务和+JNDI

    - **Microsoft Active Directory (AD)**:微软的企业级目录服务。 - **Sun ONE Directory Server**:Sun公司提供的目录服务。 - **Novell NDS**:Novell公司的目录服务。 **LDAP(Lightweight Directory Access ...

    java使用ldap修改ad域用户密码

    在安装证书服务后,我们需要导出域根证书和计算机证书,以便使用 Java 语言连接到 Active Directory 域控制器。 使用 Keytool 工具 我们可以使用 Java 的 Keytool 工具来创建或导入证书库文件。 ```bash D:\...

    com.sun.jndi.ldap.jar

    JNDI的主要作用是将Java应用程序与各种不同的命名和目录服务连接起来,如DNS、NIS、Active Directory或OpenLDAP。通过JNDI,开发者可以使用统一的API来查找、操作和管理这些服务。`com.sun.jndi.ldap.jar`中的实现...

    java连接AD进行用户登陆

    1. **Active Directory (AD)**:一种由微软开发的目录服务,用于Windows Server操作系统,可以实现对用户账户、计算机、资源和服务的集中管理和控制。 2. **LDAP (Lightweight Directory Access Protocol)**:轻量级...

    JAVA JNDI免证书修改AD域密码.zip

    在IT领域,特别是Java开发中,`JAVA JNDI免证书修改AD域密码`是一个重要的主题,涉及到企业级应用与Active Directory(AD)域服务的交互。Active Directory是微软提供的目录服务,用于集中管理网络资源,如用户账户...

    java使用ldap修改ad域用户密码收集.pdf

    5、要使用 Java 修改 Active Directory 域用户密码,需要安装 Active Directory 域控制器、安装证书服务、安装 JAVA 应用程序服务器。 6、在安装证书服务的服务器中,需要导出域根证书和计算机证书,并使用 Java 的...

    数据库连接池

    - JNDI (Java Naming and Directory Interface) 不是一个专门的连接池,但它提供了一种灵活的方式来查找和管理包括数据库连接在内的各种资源。在Web应用服务器中,JNDI常被用于配置和管理数据源。 4. **BoneCP** ...

    LDAP技术,LDAP学习大全

    Windows Active Directory (AD) 是微软Windows Server操作系统中的一项关键服务,它提供了一套完整的目录服务解决方案。AD不仅实现了LDAP标准,还包含了额外的功能和服务,例如: - **集中管理**:AD允许管理员在一...

    ldap 访问AD测试

    这些文件内容组合起来,形成了一套完整的使用Java通过LDAP协议与Active Directory进行交互的教程,包括了搜索、修改、验证用户、实现单点登录和证书管理等多个方面。学习这些知识点对于任何需要在企业环境中进行用户...

    ldap 连接 AD

    总的来说,通过理解这些源代码,你可以学习到如何在Java环境中使用LDAP协议与Active Directory进行交互,这对于构建Windows网络环境中的身份验证和授权系统至关重要。这不仅加深了对LDAP和AD的理解,也有助于提升...

    LDAP 技术总结.

    无论是 Sun One Directory Server 还是 Windows Active Directory,都是 LDAP 的具体实现,为企业信息管理和访问提供了强大支持。理解并掌握 LDAP 技术对于 IT 专业人士来说至关重要,尤其是在构建和维护跨平台的...

    Ldap实例源码

    首先,源码中可能包含了使用Java LDAP API(JNDI,Java Naming and Directory Interface)来连接和操作LDAP服务器的部分。JNDI是一个接口,提供了统一的方法来访问不同的命名和目录服务,包括LDAP。通过JNDI,开发者...

    集成Websphere Application Server 和Active MQ

    这可能包括创建JNDI(Java Naming and Directory Interface)上下文,定义JMS提供者,以及配置JMS目的地(如队列或主题)。 3. **设置连接工厂**: 在WAS管理控制台中,你需要创建一个JMS连接工厂,这将用于应用程序...

    java验证AD域用户登录

    AD(Active Directory)域是由微软Windows Server操作系统提供的目录服务,用于集中管理用户账户、资源权限和网络策略。在Java中实现AD域用户登录验证,主要涉及到以下知识点: 1. **JNDI (Java Naming and ...

    DirSyncJNDI:通过使用JNDI测试DirSync

    这个项目可能是为了帮助开发者在不同目录服务之间进行数据同步,比如Active Directory和LDAP服务器。 【描述】"目录同步"是指在两个或多个目录服务之间保持数据一致性的过程。这在分布式系统和企业环境中尤其重要,...

Global site tag (gtag.js) - Google Analytics