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 以外的其他内容对用户进行身份验证,例如使用 uid
或 sAMAccountName
。
要做的步骤是:
-
连接到 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中的用户密码?的主要内容,如果未能解决你的问题,请参考以下文章
如何使用给定的屏幕坐标选择元素 - selenium,java
java - 如何在hibernate java中为给定的xml使用注释