Comment vérifier le mot de passe utilisateur dans ldap avec java avec LdapContext donné?
J'ai une application web, où les utilisateurs doivent se connecter. Le mot de passe est stocké dans un serveur LDAP. Toutes les informations sur le serveur LDAP sont stockées dans le serveur d'applications (glassfish) en tant que ressource jndi externe. Donc, mon application ne sait rien du serveur LDAP et n'obtient qu'un LdapContext comme ceci:
@Resource(name = "ldap/users")
private LdapContext ctx;
Dans ce contexte, il est facile de modifier ou de lire les informations stockées pour les utilisateurs, mais comment vérifier leurs mots de passe? Normalement je ferais juste un nouveau connexion pour vérifier un mot de passe utilisateur. Comme ceci:
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);
Mais comme je ne connais pas les paramètres de this, je ne peux pas le faire. Alors, comment vérifier si le mot de passe d'un utilisateur est correct avec mon LdapContext? Les mots de passe sont stockés cryptés (ssha) donc je ne peux pas simplement comparer les attributs.
Merci Raffael
4 answers
Vous devriez pouvoir obtenir l'environnement à partir du contexte ldap, le cloner, puis mettre le principal et les informations d'identification pour l'utilisateur que vous souhaitez vérifier:
@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);
Il s'agit d'une solution qui peut être utilisée pour authentifier un utilisateur avec autre chose que le DN, par exemple avec un uid
ou sAMAccountName
.
Les étapes à suivre sont:
- Connexion au serveur LDAP
- Authentifier avec un utilisateur de service dont nous connaissons le DN et les informations d'identification
- Recherchez l'utilisateur que vous souhaitez authentifier, recherchez-le avec un attribut (par exemple
sAMAccountName
) - Obtenir le DN de l'utilisateur que nous avons trouvé
- Ouvrir une autre connexion au serveur LDAP avec le DN trouvé et le mot de passe
- Si l'utilisateur est trouvé et que l'authentification fonctionne, tout va bien
Exemple de code:
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;
}
J'ai fait de même dans ma demande. voici la qui pourraient être utiles pour vous.
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);
}
}
Dans les serveurs LDAP d'application réelle, le mot de passe est stocké sous forme de hashcode et chaque fois qu'un gestionnaire d'accès prend le mot de passe de l'utilisateur, ce mot de passe en texte brut est à nouveau haché avec la même clé et vérifié à celui stocké dans le LDAP. Donc, en tant que tel, vous ne pouvez pas obtenir le mot de passe simple du serveur LDAP. Donc, si vous connaissez la clé secrète, alors seulement vous pouvez la déchiffrer.