Как проверить пароль пользователя в ldap с java с данным LdapContext?
У меня есть веб-приложение, где пользователи должны войти в систему. Пароль хранится на сервере LDAP. Вся информация о сервере LDAP хранится на сервере приложений (glassfish) в качестве внешнего ресурса jndi. Поэтому мое приложение ничего не знает о сервере 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), поэтому я не могу просто сравнить атрибуты.
Спасибо
Раффаэль
Ответы
Ответ 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 пользователя, которого мы обнаружили
- Откройте другое соединение с сервером LDAP с найденным DN и паролем
- Если пользователь найден и аутентификация работает, вы в порядке
Пример кода:
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;
}
Ответ 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
Эта тема помогла мне решить мое дело, спасибо Брэндон и mel3kings заставляют меня думать.
Ответ 5
В реальных серверах LDAP-приложения пароль хранится в форме hashcode и всякий раз, когда любой диспетчер доступа принимает пароль от пользователя, этот простой текстовый пароль снова хэшируется с тем же ключом и проверяется на том, что хранится в LDAP. Таким образом, вы не можете получить простой пароль с сервера LDAP.
Поэтому, если вы знаете секретный ключ, только тогда вы можете его расшифровать.