【问题标题】:How to check user password in ldap whith java with given LdapContext?java - 如何使用给定的LdapContext检查ldap中的用户密码?
【发布时间】: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),所以我不能只比较属性。

谢谢 拉斐尔

【问题讨论】:

    标签: java authentication web-applications ldap


    【解决方案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

      要做的步骤是:

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

      代码示例:

      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 服务器获取纯密码。 所以如果你知道秘钥,只有这样你才能解密它。

        【讨论】:

        • 您无法解密密码哈希,也没有“密钥”。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-12-12
        • 1970-01-01
        • 2013-02-06
        • 1970-01-01
        • 2020-04-27
        • 1970-01-01
        相关资源
        最近更新 更多