简体   繁体   中英

Authentication against remote active directory in java on Linux

My scenario is similar to what's described in this question How to authenticate users with a customer's (remote) active directory server , except that our web application is Java based and runs under CentOS Linux.

I have been searching and reading a lot, but don't seem to be able to find useful information for my scenario because of the following:

  1. Although there are different solutions for Java based application to authenticate against AD (Kerberos, NTLM, LDAP from Authenticating against Active Directory with Java on Linux ), but I don't think they are for remote AD authentication from what I read.
  2. Kerberos is said to be the "new" mechanism for AD authentication recommended by Microsoft. However, it seems that either conf files or system properties are required for it to work. Our web application is SaaS based so the same code base will server multiple customers, and I don't see a good way to make Kerberos work nicely (even if it support remote authentication, which I don't see).
  3. Product Jespa uses NTLMv2 based authentication (again, I am not seeing it supports remote authentication), which is "old" and not recommended by Microsoft anymore. Plus it requires the creation of a "computer account".
  4. Okta has a solution that will work for the remote scenario I am facing. However, it requires the installation of an "AD agent" on customers AD server to handle all SAML communications between the "AD agent" and a Java library sitting with our web application. It's definitely a concern for our customers to install a software in their infrastructure that they are not a customer of.

Now based on my recent reading, best security practices are for organizations to hide Active Directory server behind firewall and do not expose it to the Internet. I wonder whether that means there will not be "direct" path of remote Active Directory authentication integration regardless of tools and libraries, and some kind of trusted agent or proxy residing within the customer infrastructure will be required to facilitate the remote authentication.

Any comments and suggestions are welcome!

We can access remote AD via LDAP protocol. In my scenario, I have LDAP configured on remote server. I have installed Apache Active DS to create an instance of remote directory at my end. So I can have a configuration details at my end. To do this we require domain name & bind credentials . You can download Apache Directory for Linux from this link & create new connection. See LDAP Connections to Active Directory Using Apache Directory Studio

LDAP Configuration for Tomcat :

Sample JNDIRealm configuration for Tomcat 7 - Apache.org

Add your desired Realm configuration between Host opening and Host closing tag in server.xml file located at conf directory of Tomcat. eg,

<Host name="localhost"  appBase="webapps"
        unpackWARs="true" autoDeploy="true">
    <Realm   className="org.apache.catalina.realm.JNDIRealm"
        connectionName="cn=Manager,dc=mycompany,dc=com"
        connectionPassword="secret"
        connectionURL="ldap://localhost:389"
        userPassword="userPassword"
        userPattern="uid={0},ou=people,dc=mycompany,dc=com"
        roleBase="ou=groups,dc=mycompany,dc=com"
        roleName="cn"
        roleSearch="(uniqueMember={0})"
    />
    <!-- other stuffs -->
</Host>

Don't forget to comment out the below entry in server.xml file.

<Realm className="org.apache.catalina.realm.UserDatabaseRealm"
           resourceName="UserDatabase"/>

Once you are done with this then it's time to configure your web application which will give a call to LDAP server via Tomcat server.

Web Application Configuration :

Add below entries in your web.xml file placed inside WebContent -> WEB-INF directory.

<security-constraint>
    <web-resource-collection>
          <web-resource-name>Logging Area</web-resource-name>
          <description>
              Authentication Required.
          </description>
          <url-pattern>/*</url-pattern>
          <http-method>GET</http-method>
          <http-method>POST</http-method>
    </web-resource-collection>
        <auth-constraint>
            <role-name>*</role-name>
        </auth-constraint>
    </security-constraint>
   <security-role>
      <role-name>*</role-name>
   </security-role>
    <login-config>
          <auth-method>BASIC</auth-method>
        <realm-name>Authentication Required.</realm-name>
</login-config>

This is the basic configuration. But, you can map specific user or group with LDAP . Have a look at Mapping Roles to Users and Groups - J2EE Tutorial . You can also add security to specific folder only, by adding url pattern for it under web-resource-collection tags.

<url-pattern>/Admin/*</url-pattern> // Restrict access to Admin folder
<url-pattern>/Employee/*</url-pattern>  // Restrict access to Employee folder

All configuration are done. Now, when your run you application it will ask for Login Credentials . Once you enter & hit submit it will search for that user in LDAP server.

Tomcat LDAP认证窗口

At server, to get access to user name use below code snippet :-

Principal principal = request.getUserPrincipal();
String userName = principal.getName();

Example LDAP User Manager (JBOSS):

public LdapContext getLdapContext() {
    // Set up environment for creating initial context
    Hashtable<String, Object> env = new Hashtable<String, Object>(11);

    env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
    // e.g., ldap://IP address of remote m/c:10389/dc=sevenseas,dc=com
    env.put(Context.PROVIDER_URL, LDAP_PROVIDER_URL + Constant.FORWARD_SLASH + LDAP_DOMAIN);

    // Authenticate as User and password
    env.put(Context.SECURITY_AUTHENTICATION, "simple");
    // e.g., "uid=admin,ou=system"
    env.put(Context.SECURITY_PRINCIPAL, LDAP_ADMIN);
    env.put(Context.SECURITY_CREDENTIALS, LDAP_DEFAULT_PASSWORD);

    try {
        // Create initial context
        ldapContext = new InitialLdapContext(env, null);
        if (ldapContext == null) {
            LogManager.fatal("Invalid LDAP system properties. Please contact your administrator.",
                    LDAPUserManager.class.getName());
        }
        System.out.println("Organization : " + ldapContext.getNameInNamespace());
    } catch (Exception e) {
        StringWriter stack = new StringWriter();
        e.printStackTrace(new PrintWriter(stack));
        LogManager.fatal(stack.toString(), LDAPUserManager.class.getName());
    }
    return ldapContext;
}

This will Construct initial context with environment parameters to connect with LDAP server.

Now, to fetch user data from AD use the below code :

try {
        SearchControls constraints = new SearchControls();
        constraints.setSearchScope(SearchControls.SUBTREE_SCOPE);
        String[] attrIDs = { "cn", "sn", "givenName", "uid", "mail", "userPassword" };

        constraints.setReturningAttributes(attrIDs);

        // Search for user in LDAP server by emailId
        NamingEnumeration<SearchResult> answer = ldapContext.search(LDAP_GROUP, "mail=" + emailId, constraints);
        if (answer.hasMore()) {
            LogManager.info("User with email id '" + emailId + "' found.", LDAPUserManager.class.getName());

            Attributes attrs = answer.next().getAttributes();

            userDTO = new UserDTO();

            // Store user details temporarily
            Attribute firstNameAttr = attrs.get("givenName");
            String firstName = "";
            if (firstNameAttr == null) {
                userDTO.setFirstName(firstName);
            } else {
                firstName = firstNameAttr.get().toString();
                if (firstName == null) {
                    userDTO.setFirstName("");
                }
                userDTO.setFirstName(firstName);
            }

            Attribute lastNameAttr = attrs.get("sn");
            String lastName = "";
            if (lastNameAttr == null) {
                userDTO.setLastName(lastName);
            } else {
                lastName = lastNameAttr.get().toString();
                if (lastName == null) {
                    userDTO.setLastName("");
                }
                userDTO.setLastName(lastName);
            }

            return userDTO;
        } else {
            LogManager.fatal("Invalid User.", LDAPUserManager.class.getName());
            return userDTO;
        }
    } catch (Exception e) {
        StringWriter stack = new StringWriter();
        e.printStackTrace(new PrintWriter(stack));
        LogManager.fatal(stack.toString(), LDAPUserManager.class.getName());
    }

I hope this helps you.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM