java - 如何使用给定的LdapContext检查ldap中的用户密码?

Posted

技术标签:

【中文标题】java - 如何使用给定的LdapContext检查ldap中的用户密码?【英文标题】:How to check user password in ldap whith java with given LdapContext? 【发布时间】:2011-02-01 03:22:15 【问题描述】:

我确实有一个网络应用程序,用户必须在其中登录。密码存储在 LDAP 服务器中。有关 LDAP 服务器的所有信息都作为外部 jndi 资源存储在应用程序服务器 (glassfish) 中。所以我的应用程序对 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);

但是由于我不知道 this 参数,所以我不能这样做。那么如何使用我的 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 以外的其他内容对用户进行身份验证,例如使用 uidsAMAccountName

要做的步骤是:

    连接到 LDAP 服务器 向我们知道其 DN 和凭据的服务用户进行身份验证 搜索你要认证的用户,用一些属性搜索他(例如sAMAccountName) 获取我们找到的用户的 DN 使用找到的 DN 和密码打开另一个与 LDAP 服务器的连接 如果找到用户并且身份验证有效,那就没问题

代码示例:

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;

【讨论】:

您好 Nikolay,我也认为这是首选方法,因为您可以按不同条件正确搜索用户。不过我试过了,检索distinguishedName 属性总是返回null。我认为使用result.getNameInNamespace() 是更好的选择,你觉得呢? 您好 Max,感谢您完善我的解决方案。关于获取 DN,我相信它可能取决于 LDAP 服务提供商。以我 5 年前的经典 Microsoft AD LDAP 为例,它通过提取属性 attrs.get("distinguishedName") 工作。【参考方案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】:

在实际的应用程序 LDAP 服务器中,密码以哈希码形式存储,每当任何访问管理器从用户那里获取密码时,该纯文本密码都会再次使用相同的密钥进行哈希处理,并检查存储在 LDAP 中的密码。因此,您无法从 LDAP 服务器获取纯密码。 所以如果你知道秘钥,只有这样你才能解密它。

【讨论】:

您无法解密密码哈希,也没有“密钥”。

以上是关于java - 如何使用给定的LdapContext检查ldap中的用户密码?的主要内容,如果未能解决你的问题,请参考以下文章

LdapContext获取对象的属性

如何使用给定的屏幕坐标选择元素 - selenium,java

java - 如何在hibernate java中为给定的xml使用注释

如何使用java检测给定的日期格式

java - 如何使用PriorityQueue集合从Java中的给定数组在O(n)时间内构建最大堆?

java如何打印出任意给定的空格