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

我有一个Web应用程序,用户必须登录。密码存储在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); 

但由于我不知道这个参数我不能这样做。 那么如何使用我的LdapContext检查用户的密码是否正确? 密码以加密方式存储(ssha),所以我不能只比较属性。

谢谢拉斐尔

您应该能够从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); 

这是一种解决方案,可用于使用除DN之外的其他内容对用户进行身份validation,例如使用uidsAMAccountName

要做的步骤是:

  1. 连接到LDAP服务器
  2. 与我们了解DN和凭据的服务用户进行身份validation
  3. 搜索要进行身份validation的用户,使用某个属性搜索他(例如sAMAccountName
  4. 获取我们找到的用户的DN
  5. 使用找到的DN和密码打开与LDAP服务器的另一个连接
  6. 如果找到用户并且身份validation有效,那么您没问题

代码示例:

 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 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; } 

我在我的申请中做了同样的事情。 以下是可能对您有用的内容。

  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); } } 

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