Get instance of DataSourceRealm from a servlet

2019-06-10 03:28发布

问题:

I am using tomcat 8.0. I configured a realm element in the context.xml file which specifies that I will be implementing the DataSourceRealm. In addition, as per the tomcat 8 realm configuration instructions (https://tomcat.apache.org/tomcat-8.0-doc/realm-howto.html) I nested a CredentialHandler element in the realm element in order to specify attributes such as salt length and iterations. The relevant portion of the context.xml file is as follows:

<Realm className="org.apache.catalina.realm.DataSourceRealm" debug="99"
    dataSourceName="jdbc/board" localDataSource="true"
    userTable="test_user" userNameCol="Email" userCredCol="HashedPassword"
    userRoleTable="test_user_role" roleNameCol="Role">

        <CredentialHandler className="MessageDigestCredentialHandler" algorithm="SHA-1"
            iterations="1000" saltLength="48"/>

    </Realm>

When I call a servlet in my web application, I would like to be able to reference the above realm object so that I can call non-static methods of the RealmBase class (eg: digest() (Not the static Digest() method)). I would like to call the digest method of the initialized realm object because that is the object that contains all of the attributes that I specified (salt length, etc.). How can I get access to the DataSourceRealm object from a servlet? (Calling the static method and manually specifying the hashing algorithm doesn't seem logical, not to mention the fact that there is no parameter for inputting the salt details in the static method).

I tried searching the ServletContext and HttpServletRequest API's for a method for retrieving the RealmBase Object or its Container Object but didn't find anything relevant.

Edit: I tried getting the InitialContext object and using the lookup method since this is what I use to get the resource element that's also located in the context.xml file:

InitialContext ic = new InitialContext();
            DataSourceRealm realm = (DataSourceRealm) ic.lookup("org.apache.catalina.realm.DataSourceRealm");

But this also didn't work.

Thank you

回答1:

in Context applications we don't use class (equivalent), i.e. not ic.lookup("org.apache.catalina.realm.DataSourceRealm")

but name ic.lookup("jdbc/board")

(this is not tested, only way of solution)



回答2:

This is an old(er) question, but it is possible if you're willing to use reflection. The following code will retrieve the configured CredentialHandler from the ServletContext:

public CredentialHandler getCredentialHandler(ServletContext context) {

    Realm realm = getRealm(context);

    if (realm != null) {

        return realm.getCredentialHandler();
    }

    return null;
}

private Realm getRealm(ServletContext context) {

    if (context instanceof ApplicationContextFacade) {

        ApplicationContext applicationContext = getApplicationContext(
                (ApplicationContextFacade)context
        );

        if (applicationContext != null) {

            StandardContext standardContext = getStandardContext(
                    applicationContext
            );

            if (standardContext != null) {

                return standardContext.getRealm();
            }
        }
    }

    return null;
}

private ApplicationContext getApplicationContext(
        ApplicationContextFacade facade) {

    try {

        Field context = ApplicationContextFacade.class.getDeclaredField(
               "context"
        );

        if (context != null) {

            context.setAccessible(true);
            Object obj = context.get(facade);

            if (obj instanceof ApplicationContext) {

                return (ApplicationContext)obj;
            }
        }

    } catch (Exception ex) {
    }

    return null;
}

private StandardContext getStandardContext(
        ApplicationContext applicationContext) {

    try {

        Field context = ApplicationContext.class.getDeclaredField(
                "context"
        );

        if (context != null) {

            context.setAccessible(true);
            Object obj = context.get(applicationContext);

            if (obj instanceof StandardContext) {

                return (StandardContext)obj;
            }
        }

    } catch (Exception ex) {
    }

    return null;
}

You can call this early in the application, say in a ServletContextListener or ServletContainerInitializer and store the handler for later use.

Ugly, but I don't think there's another way.

Dave