zoukankan      html  css  js  c++  java
  • 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
  • 相关阅读:
    CODING x 百果园 _ 水果零售龙头迈出 DevOps 体系建设第一步
    Nocalhost 亮相 CD Foundation 国内首届 Meetup,Keith Chan 将出席致辞
    做云原生时代标准化工具,实现高效云上研发工作流
    打造数字化软件工厂 —— 一站式 DevOps 平台全景解读
    WePack —— 助力企业渐进式 DevOps 转型
    CODING Compass —— 打造行云流水般的软件工厂
    Nocalhost —— 让云原生开发回归原始而又简单
    CODING 代码资产安全系列之 —— 构建全链路安全能力,守护代码资产安全
    Nocalhost:云原生开发新体验
    使用 Nocalhost 开发 Kubernetes 中的 APISIX Ingress Controller
  • 原文地址:https://www.cnblogs.com/xiaomaohai/p/6157386.html
Copyright © 2011-2022 走看看