How can you store credentials in a JNDI lookup?

2020-07-25 00:57发布

问题:

I have an app with several properties files, and I was curious if it'd be easier to store these username/password/server-url-to-login-to combos in a JNDI lookup instead of this clear text file in a .jar.

There are a couple questions:

  1. Can you store credentials that aren't databases using JNDI?
  2. How do you configure a new JNDI resource to store these properties?
  3. How would you retrieve these properties from the resource?

Most of the documentation I read was how to set up JNDI for database connections, and for my use, I'm not connecting to any databases, but instead different kinds of web services using different authentication schemes (SOAP user/password in request message, HTTP Basic, headers, SSL, etc.).

回答1:

The answers to your questions:

  1. Can you store credentials that aren't databases using JNDI?

Yes.

  1. How do you configure a new JNDI resource to store these properties?

If you are using Glassfish 2 you have to create your custom PropertiesObjectFactory class to handle JNDI properties of java.util.Porperties.

For e.g. PropertiesObjectFactory class could look like this:

public class PropertiesObjectFactory implements Serializable, ObjectFactory {

    public static final String FILE_PROPERTY_NAME = "org.glassfish.resources.custom.factory.PropertiesFactory.fileName";

    public Object getObjectInstance(Object obj, Name name, Context nameCtx, Hashtable<?, ?> environment)
            throws Exception {
        Reference ref = (Reference) obj;
        Enumeration<RefAddr> refAddrs = ref.getAll();

        String fileName = null;
        Properties fileProperties = new Properties();
        Properties properties = new Properties();

        while (refAddrs.hasMoreElements()) {
            RefAddr addr = refAddrs.nextElement();
            String type = addr.getType();
            String value = (String) addr.getContent();

            if (type.equalsIgnoreCase(FILE_PROPERTY_NAME)) {
                fileName = value;
            } else {
                properties.put(type, value);
            }
        }

        if (fileName != null) {
            File file = new File(fileName);
            if (!file.isAbsolute()) {
                file = new File(System.getProperty("com.sun.aas.installRoot") + File.separator + fileName);
            }
            try {
                if (file.exists()) {
                    try {
                        FileInputStream fis = new FileInputStream(file);
                        if (fileName.toUpperCase().endsWith("XML")) {
                            fileProperties.loadFromXML(fis);
                        } else {
                            fileProperties.load(fis);
                        }
                    } catch (IOException ioe) {
                        throw new IOException("IO Exception during properties load : " + file.getAbsolutePath());
                    }
                } else {
                    throw new FileNotFoundException("File not found : " + file.getAbsolutePath());
                }
            } catch (FileNotFoundException fnfe) {
                throw new FileNotFoundException("File not found : " + file.getAbsolutePath());
            }
        }
        fileProperties.putAll(properties);
        return fileProperties;
    }
}

Make a jar of that class, add it into glassfish global classpath. It would be: /glassfish/domains/domain1/lib and then you can specify it as a factory class in your JNDI properties configuration.

Glassfish 3 already have properties factory class. It is set into: org.glassfish.resources.custom.factory.PropertiesFactory.

Open glassfish admin console and navigate to: Resources -> JNDI -> Custom Resources, click "New", provide a JNDI name, for e.g: jndi/credentials, choose a resource type java.util.Properties, specify Factory class: org.glassfish.resources.custom.factory.PropertiesFactory, then click "Add property", specify name for e.g: testUsernameName and in value column testUsernameValue. Click OK and that is all, you have configured JNDI resource. You can add as many properties as you like to this: jndi/credentials resource.

Don't forget to restart app server when you done creating resources.

  1. How would you retrieve these properties from the resource?
public Properties getProperties(String jndiName) {
    Properties properties = null;
    try {
        InitialContext context = new InitialContext();
        properties = (Properties) context.lookup(jndiName);
        context.close();
    } catch (NamingException e) {
        LOGGER.error("Naming error occurred while initializing properties from JNDI.", e);
        return null;
    }
    return properties;
}

Example how to get the property:

String username = someInstance.getProperties("jndi/credentials").getProperty("testUsernameName"); 

Your username would be: testUsernameValue.

When you call this method in your application provide a JNDI name you configured in your application server: jndi/credentials. If you have mapped resources in deployment descriptor you have to use: java:comp/env/jndi/credentials.

If you want to do the same using Spring:

<jee:jndi-lookup id="credentials"
                 jndi-name="jndi/credentials"/>

Well I hope thats it. Hope this helps.