列出域中的Ldap服务器并进行身份验证

时间:2011-06-30 13:12:30

标签: java authentication active-directory ldap

如何获取给定域名的LDAP服务器列表(使用java + acitvedirectory),并根据用户名和密码对其进行身份验证?

3 个答案:

答案 0 :(得分:6)

以下是执行此操作的步骤

GET服务器列表

  1. 点击DNS服务器以获取SRV记录
  2. 排序SRV记录
  3. 根据正则表达式模式过滤服务器记录
  4. 根据我们是否需要ssl来构建LDAP网址。 注意 srv记录只能返回一个端口,所以不要依赖从srv记录返回的端口。请参阅srv record RFC
  5. 验证

    1. 遍历服务器列表
      1. 使用ldap环境构造Hashtable。请注意,需要使用由\分隔的用户名附加域名,并向其添加ldap URL
      2. 尝试创建InitialDirContext
      3. 成功:关闭上下文并返回
      4. failure1:如果命名异常是AuthenticationException并且包含messge “[LDAP:错误代码49”翻译并抛出可读异常!见error code mapping
      5. failure2:如果没有,则继续使用下一个ldap网址

    2. 这是完整的工作代码

      如果您在conf之后使用spring来调用init方法

      <bean
          class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
          <property name="targetClass">
              <value>org.bhate.ldap.LdapUtil</value>
          </property>
          <property name="targetMethod" value="init" />
          <property name="arguments">
              <list>
                  <value>${ldap.dnsServer}</value>
                  <value>${ldap.domainName}</value>
                  <value>${ldap.filter.regexp}</value>
                  <value>${ldap.ssl}</value>
              </list>         
          </property>
      

      以下属性

      ldap.dnsServer=uk.mydomain.com
      ldap.domainName=DOMAINNAME
      ldap.filter.regexp=serv10.*|server.*
      ldap.ssl=true
      

      在启动时调用init

      LdapUtil.init("uk.mydomain.com", "DOMAINNAME", "serv10.*|server.*",true);
      

      LdapUtil.java

      package org.bhate.ldap;
      
      import java.util.Collection;
      import java.util.HashMap;
      import java.util.Hashtable;
      import java.util.LinkedHashSet;
      import java.util.Map;
      import java.util.TreeSet;
      import java.util.regex.Pattern;
      
      import javax.naming.AuthenticationException;
      import javax.naming.Context;
      import javax.naming.NamingException;
      import javax.naming.directory.Attribute;
      import javax.naming.directory.Attributes;
      import javax.naming.directory.DirContext;
      import javax.naming.directory.InitialDirContext;
      import javax.naming.spi.NamingManager;
      
      import org.slf4j.Logger;
      import org.slf4j.LoggerFactory;
      import org.springframework.core.NestedExceptionUtils;
      import org.springframework.security.BadCredentialsException;
      
      
      public class LdapUtil {
      
          private static final Logger LOGGER = LoggerFactory
                  .getLogger(LdapUtil.class);
      
          @SuppressWarnings("serial")
          final static Map<String, String> errorCodesMap = new HashMap<String, String>() {
              {
                  put("525", "user not found");
                  put("52e", "invalid credentials");
                  put("530", "not permitted to logon at this time");
                  put("531", "not permitted to logon at this workstation");
                  put("532", "password expired");
                  put("533", "account disabled");
                  put("701", "account expired");
                  put("773", "user must reset password");
                  put("775", "user account locked");
              }
          };
      
          private static Collection<String> ldapServers;
          private static String domainName;
          private static Pattern PATTERN;
      
          public static DirContext getDirContext(String url, String domainName,
                  String userName, String password) throws NamingException {
              Hashtable<String, Object> env = getEnv(url, domainName, userName,
                      password);
              return new InitialDirContext(env);
          }
      
          public static Collection<String> getLdapServers(String ldapDomain,
                  boolean useSsl) throws NamingException {
              Collection<String> serverRecords = getSRVRecords(ldapDomain);
              serverRecords = reOrder(serverRecords);
              Collection<String> serverNames = new LinkedHashSet<String>();
              String protocol = "ldap" + (useSsl ? 's' : "");
              for (String s : serverRecords) {
                  String hostName = s.substring(s.lastIndexOf(' ') + 1,
                          s.length() - 1);
                  serverNames.add(protocol + "://" + hostName);
              }
              return serverNames;
          }
      
          private static Collection<String> reOrder(Collection<String> serverRecords) {
              return serverRecords;
          }
      
          private static Collection<String> getSRVRecords(String ldapDomain)
                  throws NamingException {
              DirContext context = (DirContext) NamingManager.getURLContext("dns",
                      new Hashtable<String, Object>());
              String ldapDNSUrl = "dns:///_ldap._tcp." + ldapDomain;
              String[] attrIds = { "SRV" };
              Attributes attributes = context.getAttributes(ldapDNSUrl, attrIds);
              Attribute servers = attributes.get("SRV");
              int L = servers.size();
              Collection<String> serverRecords = new TreeSet<String>();
              for (int i = 0; i < L; i++) {
                  String s = (String) servers.get(i);
                  if (PATTERN.matcher(s).find())
                      serverRecords.add(s);
              }
              return serverRecords;
          }
      
          public static void authenticate(String userName, String password) {
              String msg = "Unable authenticate user {} with {}";
              for (String url : ldapServers) {
                  DirContext ctx = null;
                  try {
                      ctx = getDirContext(url, domainName, userName, password);
                      LOGGER
                              .info("Authenticated user {} on server {}", userName,
                                      url);
                      return;
                  } catch (NamingException e) {
                      LOGGER.error(msg, userName, url);
                      String m = NestedExceptionUtils.buildMessage(e.getMessage(), e
                              .getCause());
                      LOGGER.error(m, e);
                      if (e instanceof AuthenticationException
                              && e.getMessage().startsWith("[LDAP: error code 49"))
                          throwMeanigfulEx(userName, url, e);
                  } finally {
                      close(ctx);
                  }
              }
              LOGGER.error(msg, userName, "any available server");
              throw new BadCredentialsException(
                      "Unable to authenticate : Please contact application support team");
          }
      
          public static void init(String dnsServerName, String domainName,
                  String serverFilter, boolean useSsl) {
              PATTERN = Pattern.compile(serverFilter);
              try {
                  LdapUtil.domainName=domainName;
                  ldapServers = getLdapServers(dnsServerName, useSsl);
      
                  if (LOGGER.isInfoEnabled()) {
                      LOGGER.info(
                              "LDAP servers available in domain {} to connect {}",
                              dnsServerName, ldapServers);
                  }
              } catch (NamingException e) {
                  throw new RuntimeException("Unable retrieve ldapServers for "
                          + dnsServerName, e);
              }
          }
      
          private static void throwMeanigfulEx(String userName, String url,
                  NamingException e) {
              String separator = ", data ";
              String m = e.getMessage();
              int strt = m.lastIndexOf(separator) + separator.length();
              int end = m.lastIndexOf(", vece");
              String code = m.substring(strt, end);
              throw new BadCredentialsException("Unable to authenticate : "
                      + errorCodesMap.get(code));
          }
      
          private static void close(DirContext ctx) {
              if (ctx != null)
                  try {
                      ctx.close();
                  } catch (NamingException e) {
                      LOGGER.error("Unable to close context", e);
                  }
          }
      
          private static Hashtable<String, Object> getEnv(String url,
                  String domainName, String userName, String password) {
              Hashtable<String, Object> env = new Hashtable<String, Object>();
              env.put(Context.INITIAL_CONTEXT_FACTORY,
                      "com.sun.jndi.ldap.LdapCtxFactory");
              env.put(Context.REFERRAL, "follow");
              env.put(Context.PROVIDER_URL, url);
              env.put("com.sun.jndi.ldap.connect.timeout", "2000");
              // env.put("com.sun.jndi.ldap.trace.ber", System.err);
              // env.put("javax.net.ssl.trustStoreType", "JKS");
              // env.put(Context.SECURITY_PROTOCOL, "ssl");
              env.put(Context.SECURITY_AUTHENTICATION, "simple");
              env.put(Context.SECURITY_PRINCIPAL, getPrincipal(domainName, userName));
              env.put(Context.SECURITY_CREDENTIALS, password);
              return env;
          }
      
          private static Object getPrincipal(String domainName, String userName) {
              return domainName + "\\" + userName;
          }
      
      
          public static void main(String[] args) throws NamingException {
              init("uk.mydomain.com", "DOMAINNAME", "serv10.*|server.*",true);
              System.out.println(ldapServers.size());
              System.out.println(ldapServers);
              if (args.length == 2)
                  authenticate(args[0], args[1]);
          }
      
      
          //
          // public static InitialLdapContext getLdapContext(String userName,
          // String domainName, String password, String url)
          // throws NamingException {
          // Hashtable<String, Object> env = getEnv(url, domainName, userName,
          // password);
          // Control[] connCtls = new Control[] { new Control() {
          // private static final long serialVersionUID = 1L;
          //
          // public byte[] getEncodedValue() {
          // return null;
          // }
          //
          // public String getID() {
          // return "1.2.840.113556.1.4.1781";
          // }
          //
          // public boolean isCritical() {
          // return true;
          // }
          // } };
          // return new InitialLdapContext(env, connCtls);
          // }
      
      
      }
      

答案 1 :(得分:2)

以下是在Java中查找SRV记录的片段,将其与ShaMan(_ldap._tcp.dc._msdcs.your.domain.com)提供的域一起使用。

private static final String[] SRV = new String[] { "SRV" };

public static Collection<InetSocketAddress> srv(String name)
    throws NamingException
{
    DirContext ctx = new IntialDirContext();

    Attributes attrs = ctx.getAttributes("dns:/" + name, SRV);
    if(attributes.get("SRV") == null)
    {
        return Collections.emptyList();
    }

    NamingEnumeration<?> e = attributes.get("SRV").getAll();
    TreeMap<Integer, InetSocketAdress> result = new TreeMap<Integer, InetSocketAdress>();

    while(e.hasMoreElements())
    {
        String line = (String) e.nextElement();

        // The line is priority weight port host
        String[] parts = line.split("\\s+");

        int prio = Integer.parseInt(parts[0]);
        int port = Integer.parseInt(parts[2]);
        String host = parts[3];

        result.put(prio, new InetSocketAddress(host, port));
    }

    return result.values();
}

答案 2 :(得分:1)

您可以通过读取该域的SRV记录来获取给定AD域的LDAP服务器列表。您需要的SRV记录具有与此_ldap._tcp.dc._msdcs.your.domain.com类似的值。本文应该为您提供更多信息:http://technet.microsoft.com/en-us/library/cc738991%28WS.10%29.aspx

您可以使用JNDI检索DNS信息,稍后通过LDAP进行身份验证。可以在这里找到一个很好的教程:http://download.oracle.com/javase/jndi/tutorial/