Как проверить пароль пользователя в ldap с java с данным LdapContext?

У меня есть веб-приложение, где пользователи должны войти в систему. Пароль хранится на сервере LDAP. Вся информация о сервере LDAP хранится на сервере приложений (glassfish) в качестве внешнего ресурса jndi. Поэтому мое приложение ничего не знает о сервере LDAP и получает только LdapContext:

@Resource(name = "ldap/users")
private LdapContext ctx;

В этом контексте легко изменить или прочитать информацию, хранящуюся для пользователей, но как я могу проверить их пароли? Обычно я просто создавал новое соединение для проверки пароля пользователя. Вот так:

Hashtable env = new Hashtable();
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
env.put(Context.PROVIDER_URL, "ldap://localhost:389/o=JNDITutorial");

env.put(Context.SECURITY_AUTHENTICATION, "simple");
env.put(Context.SECURITY_PRINCIPAL, "cn=S. User, ou=NewHires, o=JNDITutorial");
env.put(Context.SECURITY_CREDENTIALS, "mysecret");

DirContext ctx = new InitialDirContext(env);

Но так как я не знаю этих параметров, я не могу этого сделать. Итак, как я могу проверить, правильно ли пароль пользователя с моим LdapContext? Пароли хранятся в зашифрованном виде (ssha), поэтому я не могу просто сравнить атрибуты.

Спасибо Раффаэль

Ответы

Ответ 1

Вы должны иметь возможность получить среду из контекста ldap, клонировать ее, а затем поместить основной и учетные данные для пользователя, которого вы хотите проверить:

@Resource(name = "ldap/users")
private LdapContext ldapContext;

Hashtable environment = ldapContext.getEnvironment().clone();
environment.put(Context.SECURITY_PRINCIPAL, userDN);
environment.put(Context.SECURITY_CREDENTIALS, userPassword);

DirContext dirContext = new InitialDirContext(environment);

Ответ 2

Это решение, которое может использоваться для аутентификации пользователя с чем-то другим, кроме DN, например, с помощью uid или sAMAccountName.

Для этого выполните следующие действия:

  • Подключение к серверу LDAP
  • Аутентификация с пользователем службы, у которого мы знаем DN и учетные данные
  • Найдите пользователя, который вы хотите пройти проверку подлинности, найдите его с некоторым атрибутом (например, sAMAccountName)
  • Получить DN пользователя, которого мы обнаружили
  • Откройте другое соединение с сервером LDAP с найденным DN и паролем
  • Если пользователь найден и аутентификация работает, вы в порядке

Пример кода:

public static boolean performAuthentication() {

    // service user
    String serviceUserDN = "cn=Mister Service,ou=Users,dc=example,dc=com";
    String serviceUserPassword = "abc123#!$";

    // user to authenticate
    String identifyingAttribute = "uid";
    String identifier = "maxdev";
    String password = "jkl987.,-";
    String base = "ou=Users,dc=example,dc=com";

    // LDAP connection info
    String ldap = "localhost";
    int port = 10389;
    String ldapUrl = "ldap://" + ldap + ":" + port;

    // first create the service context
    DirContext serviceCtx = null;
    try {
        // use the service user to authenticate
        Properties serviceEnv = new Properties();
        serviceEnv.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
        serviceEnv.put(Context.PROVIDER_URL, ldapUrl);
        serviceEnv.put(Context.SECURITY_AUTHENTICATION, "simple");
        serviceEnv.put(Context.SECURITY_PRINCIPAL, serviceUserDN);
        serviceEnv.put(Context.SECURITY_CREDENTIALS, serviceUserPassword);
        serviceCtx = new InitialDirContext(serviceEnv);

        // we don't need all attributes, just let it get the identifying one
        String[] attributeFilter = { identifyingAttribute };
        SearchControls sc = new SearchControls();
        sc.setReturningAttributes(attributeFilter);
        sc.setSearchScope(SearchControls.SUBTREE_SCOPE);

        // use a search filter to find only the user we want to authenticate
        String searchFilter = "(" + identifyingAttribute + "=" + identifier + ")";
        NamingEnumeration<SearchResult> results = serviceCtx.search(base, searchFilter, sc);

        if (results.hasMore()) {
            // get the users DN (distinguishedName) from the result
            SearchResult result = results.next();
            String distinguishedName = result.getNameInNamespace();

            // attempt another authentication, now with the user
            Properties authEnv = new Properties();
            authEnv.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
            authEnv.put(Context.PROVIDER_URL, ldapUrl);
            authEnv.put(Context.SECURITY_PRINCIPAL, distinguishedName);
            authEnv.put(Context.SECURITY_CREDENTIALS, password);
            new InitialDirContext(authEnv);

            System.out.println("Authentication successful");
            return true;
        }
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        if (serviceCtx != null) {
            try {
                serviceCtx.close();
            } catch (NamingException e) {
                e.printStackTrace();
            }
        }
    }
    System.err.println("Authentication failed");
    return false;
}

Ответ 3

Я сделал то же самое в своем приложении. Ниже приведен пример, который может быть вам полезен.

    package com.agileinfotech.bsviewer.servlet;

    import java.io.IOException;
    import javax.servlet.RequestDispatcher;
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import javax.naming.*;
    import javax.naming.directory.*;
    import java.util.Hashtable;

    public class Login extends javax.servlet.http.HttpServlet implements javax.servlet.Servlet {

    public Login() {
    super();
    }

    protected void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

    final String SUCCESS = "loin.jsp";
    final String FAILURE = "Failure.html";
    String strUrl = "login.html";
    String username = request.getParameter("username");
    String password = request.getParameter("password");



    Hashtable env = new Hashtable(11);

    boolean b = false;

    env.put(Context.INITIAL_CONTEXT_FACTORY,
    "com.sun.jndi.ldap.LdapCtxFactory");
    env.put(Context.PROVIDER_URL, "ldap://localhost:10389");
    env.put(Context.SECURITY_AUTHENTICATION, "simple");
    env.put(Context.SECURITY_PRINCIPAL, "uid="+ username +",ou=system");
    env.put(Context.SECURITY_CREDENTIALS, password);

    try {
    // Create initial context
    DirContext ctx = new InitialDirContext(env);

    // Close the context when we're done
    b = true;
    ctx.close();

    } catch (NamingException e) {
    b = false;
    }finally{
    if(b){
    System.out.print("Success");
    strUrl = SUCCESS;
    }else{
    System.out.print("Failure");
    strUrl = FAILURE;
    }
    }
    RequestDispatcher rd = request.getRequestDispatcher(strUrl);
    rd.forward(request, response);

    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    processRequest(request,response);
    }

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    processRequest(request,response);
    } 
    }

Ответ 4

Эта тема помогла мне решить мое дело, спасибо Брэндон и mel3kings заставляют меня думать.

Ответ 5

В реальных серверах LDAP-приложения пароль хранится в форме hashcode и всякий раз, когда любой диспетчер доступа принимает пароль от пользователя, этот простой текстовый пароль снова хэшируется с тем же ключом и проверяется на том, что хранится в LDAP. Таким образом, вы не можете получить простой пароль с сервера LDAP. Поэтому, если вы знаете секретный ключ, только тогда вы можете его расшифровать.