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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2522770/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-13 08:48:09  来源:igfitidea点击:

How to check user password in ldap whith java with given LdapContext?

javaauthenticationweb-applicationsldap

提问by raffael

I do have a web-application, where users must log in. The password is stored in a LDAP server. All information about the LDAP server are stored in the application server (glassfish) as external jndi resource. So my application does no know anything about the LDAP server and only gets a LdapContext like this:

我确实有一个 Web 应用程序,用户必须在其中登录。密码存储在 LDAP 服务器中。有关 LDAP 服务器的所有信息都作为外部 jndi 资源存储在应用程序服务器 (glassfish) 中。所以我的应用程序对 LDAP 服务器一无所知,只能获得这样的 LdapContext:

@Resource(name = "ldap/users")
private LdapContext ctx;

With this context it is easy to change or read the information stored for the users, but how do i check their passwords? Normally i would just do a new connection to check a users password. Like this:

在这种情况下,更改或读取为用户存储的信息很容易,但我如何检查他们的密码?通常我只会做一个新的连接来检查用户密码。像这样:

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

But since i don't know the this parameters i can't do this. So how do i check if the password of a user is correct with my LdapContext? The passwords are stored encrypted (ssha) so i can not just compare the attributes.

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

Thanks Raffael

谢谢拉斐尔

采纳答案by Brandon

You should be able to get the environment from the ldap context, clone it, and then put the principal and credentials for the user you want to check:

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

回答by Nikolay Antipov

This is a solution that can be used to authenticate a user with something else than the DN, for example with a uidor sAMAccountName.

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

The steps to do are:

要做的步骤是:

  1. Connect to the LDAP server
  2. Authenticate with a service user of whom we know the DN and credentials
  3. Search for the user you want to authenticate, search him with some attribute (for example sAMAccountName)
  4. Get the DN of the user we found
  5. Open another connection to the LDAP server with the found DN and the password
  6. If the user is found and authentication works, you are fine
  1. 连接到 LDAP 服务器
  2. 与我们知道 DN 和凭据的服务用户进行身份验证
  3. 搜索您要验证的用户,使用某些属性搜索他(例如sAMAccountName
  4. 获取我们找到的用户的DN
  5. 使用找到的 DN 和密码打开另一个到 LDAP 服务器的连接
  6. 如果找到用户并且身份验证有效,您就可以了

Code example:

代码示例:

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

回答by Mitul Maheshwari

i have done same in my application. following is the which might be useful to you.

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

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

回答by Anil_irocks88

In real application LDAP servers, the password is stored in hashcode form and whenever any access manager takes the password from the user, that plain text password is again hashed with same key and checked to the one stored in the LDAP. So as such u can't get the plain password from LDAP server. So if you know the secret key, only then you can decrypt it.

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