如何使用给定的LdapContext检查ldap和java中的用户密码?
我有一个web应用程序,用户必须登录。密码存储在LDAP服务器中。关于LDAP服务器的所有信息都作为外部jndi资源存储在应用程序服务器(glassfish)中。因此,我的应用程序对LDAP服务器一无所知,只获得如下LdapContext:如何使用给定的LdapContext检查ldap和java中的用户密码?,java,authentication,web-applications,ldap,Java,Authentication,Web Applications,Ldap,我有一个web应用程序,用户必须登录。密码存储在LDAP服务器中。关于LDAP服务器的所有信息都作为外部jndi资源存储在应用程序服务器(glassfish)中。因此,我的应用程序对LDAP服务器一无所知,只获得如下LdapContext: @Resource(name = "ldap/users") private LdapContext ctx; Hashtable env = new Hashtable(); env.put(Context.INITIAL_CONTEXT_FACTORY
@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);
在这种情况下,很容易更改或读取为用户存储的信息,但如何检查他们的密码?
通常我只需要做一个新的连接来检查用户密码。像这样:
@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),所以我不能只比较属性
谢谢
Raffael您应该能够从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以外的其他内容对用户进行身份验证,例如使用
uid
或sAMAccountName
具体步骤如下:
sAMAccountName
)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;
}
公共静态布尔性能验证(){
//服务用户
String serviceUserDN=“cn=Mister Service,ou=Users,dc=example,dc=com”;
字符串serviceUserPassword=“abc123#!$”;
//要验证的用户
标识attribute=“uid”的字符串;
字符串标识符=“maxdev”;
字符串password=“jkl987.,-”;
String base=“ou=Users,dc=example,dc=com”;
//LDAP连接信息
字符串ldap=“localhost”;
int端口=10389;
字符串ldapUrl=“ldap://”+ldap+:“+port;
//首先创建服务上下文
DirContext serviceCtx=null;
试一试{
//使用服务用户进行身份验证
Properties serviceEnv=新属性();
serviceev.put(Context.INITIAL_Context_工厂,“com.sun.jndi.ldap.LdapCtxFactory”);
servicenev.put(Context.PROVIDER\u URL,ldapUrl);
serviceEnv.put(Context.SECURITY_身份验证,“简单”);
serviceEnv.put(Context.SECURITY\u主体,serviceUserDN);
serviceEnv.put(Context.SECURITY\u凭证、serviceUserPassword);
serviceCtx=新的InitialDirContext(serviceEnv);
//我们不需要所有的属性,只要让它得到一个可识别的属性就行了
字符串[]attributeFilter={IdentificationAttribute};
SearchControls sc=新的SearchControls();
sc.设置返回属性(属性过滤器);
sc.setSearchScope(SearchControls.SUBTREE_范围);
//使用搜索筛选器仅查找我们要验证的用户
字符串searchFilter=“(“+IdentificationAttribute+”=“+identifier+”)”;
NamingEnumeration results=serviceCtx.search(base、searchFilter、sc);
if(results.hasMore()){
//从结果中获取用户DN(DifferentiedName)
SearchResult=results.next();
字符串differentiedName=result.getNameInNamespace();
//尝试另一次身份验证,现在与用户进行身份验证
Properties authEnv=新属性();
authEnv.put(Context.INITIAL_Context_工厂,“com.sun.jndi.ldap.LdapCtxFactory”);
authEnv.put(Context.PROVIDER\u URL,ldapUrl);
authEnv.put(Context.SECURITY\u PRINCIPAL,distributedName);
authEnv.put(Context.SECURITY\u凭证、密码);
新的InitialDirContext(authEnv);
System.out.println(“身份验证成功”);
返回true;
}
}捕获(例外e){
e、 printStackTrace();
}最后{
如果(serviceCtx!=null){
试一试{
serviceCtx.close();
}捕获(NamingE例外){
e、 printStackTrace();
}
}
}
System.err.println(“身份验证失败”);
返回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服务器中,密码以哈希代码形式存储,每当任何access manager从用户处获取密码时,该纯文本密码将再次使用相同的密钥进行哈希,并检查为LDAP中存储的密码。因此,您无法从LDAP服务器获取普通密码。
因此,如果您知道密钥,只有这样您才能解密。你好,尼古拉,我也认为这是首选方法,因为您可以根据不同的标准正确搜索用户。但是我尝试了这个方法,检索
discrimitedName
属性总是返回null
。我认为使用result.getNameInNamespace()
是一个更好的选择,您认为如何?您好,Max,感谢您完善我的解决方案。关于获取DN,我相信这可能取决于LDAP服务提供商。在我5年前的例子中,在经典的Microsoft AD LDAP上,它通过提取属性attrs.get(“DifferentizedName”)
工作。您无法解密密码散列,并且没有“密钥”。