Hi, I'm upgrading an existing Vaadin 6 application from Tomcat 7.0.26
to Tomcat 8.0.32 and have some questions. I'm using Windows 7 and
debugging in Eclipse.
For authentication our configuration is using a MemoryRealm with
digest="SHA". We are storing usernames and passwords in a
tomcat-users.xml file. We are using a jaas.config which specifies to
use a org.apache.catalina.realm.JAASMemoryLoginModule. We have our
own implementation of a CallbackModule. For the upgrade I have added
a CredentialHandler node inside our Realm as suggested in the
documentation.
server.xml:
<GlobalNamingResources>
<!-- Editable user database that can also be used by
UserDatabaseRealm to authenticate users
-->
<Resource auth="Container" description="User database that can be
updated and saved"
factory="org.apache.catalina.users.MemoryUserDatabaseFactory"
name="UserDatabase" pathname="conf/tomcat-users.xml"
type="org.apache.catalina.UserDatabase"/>
</GlobalNamingResources>
...
<Realm className="org.apache.catalina.realm.MemoryRealm" digest="SHA">
<CredentialHandler algorithm="SHA"
className="org.apache.catalina.realm.MessageDigestCredentialHandler"/>
</Realm>
jaas.config:
/** JAAS Login Configuration for the Application **/
JAASTomcat {
org.apache.catalina.realm.JAASMemoryLoginModule required debug=true;
};
our LoginController:
callbackHandler = new LoginFormCallBackHandler(user);
tomcatLoginContext = new LoginContext("JAASTomcat", callbackHandler);
...
tomcatLoginContext.login();
our CallbackHandler:
public class LoginFormCallBackHandler implements CallbackHandler,
Serializable {
private static final long serialVersionUID = 1L;
private LoginEvent logins = null;
private User user;
public LoginFormCallBackHandler(User user)
{
this.user = user;
}
public void setLogins(LoginEvent loginEvent) {
logins = loginEvent;
}
public void handle(Callback[] callbacks) throws IOException,
UnsupportedCallbackException {
boolean gotName = false;
for (int i = 0; i < callbacks.length; i++) {
if (callbacks[i] instanceof NameCallback) {
// get the username from the loginform event
NameCallback nc = (NameCallback) callbacks[i];
nc.setName(logins.getLoginParameter(LoginView.LOGINFORM_PARAMETER_USERNAME));
user.username = nc.getName();
gotName = true;
} else if (callbacks[i] instanceof TextInputCallback) {
TextInputCallback cb = (TextInputCallback) callbacks[i];
if (cb.getPrompt().equals("catalinaBase")) {
cb.setText(System.getenv("CATALINA_BASE"););
}
} else if (callbacks[i] instanceof PasswordCallback) {
// get the password from the loginform event
PasswordCallback pc = (PasswordCallback) callbacks[i];
try {
MessageDigest md = MessageDigest.getInstance("SHA");
md.update(logins.getLoginParameter(LoginView.LOGINFORM_PARAMETER_PASSWORD).getBytes());
user.password = String.format("%x", new
BigInteger(1, md.digest()));
}
catch (NoSuchAlgorithmException nsae)
{
user.password =
logins.getLoginParameter(LoginView.LOGINFORM_PARAMETER_PASSWORD);
}
pc.setPassword(user.password.toCharArray());
} else {
System.out.println("Unrecognized Callback: " + callbacks[i]);
}
}
if (gotName)
logins = null;
}
}
This all worked in Tomcat 7 but unfortunately isn't working in Tomcat
8. I was getting "WARNING: Unable to determine Catalina base to load
file conf/tomcat-users.xml" and so I had to start responding to
TextInputCallback "catalinaBase" as required by the new
getCatalinaBase method on JAASMemoryLoginModule.
I got past that and now I'm getting a NullPointerException:
Authentication failed: java.lang.NullPointerException
at org.apache.catalina.realm.MemoryRealm.authenticate(MemoryRealm.java:127)
at
org.apache.catalina.realm.JAASMemoryLoginModule.login(JAASMemoryLoginModule.java:288)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at javax.security.auth.login.LoginContext.invoke(Unknown Source)
at javax.security.auth.login.LoginContext.access$000(Unknown Source)
at javax.security.auth.login.LoginContext$4.run(Unknown Source)
at javax.security.auth.login.LoginContext$4.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at javax.security.auth.login.LoginContext.invokePriv(Unknown Source)
at javax.security.auth.login.LoginContext.login(Unknown Source)
at com.lizardtech.es.adminui.vaadin.jaas.LoginController.onLogin(Unknown Source)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at com.vaadin.event.ListenerMethod.receiveEvent(ListenerMethod.java:510)
at com.vaadin.event.EventRouter.fireEvent(EventRouter.java:164)
at com.vaadin.ui.AbstractComponent.fireEvent(AbstractComponent.java:1219)
at com.vaadin.ui.LoginForm$2.handleParameters(LoginForm.java:103)
at com.vaadin.ui.Window.handleParameters(Window.java:515)
at
com.vaadin.terminal.gwt.server.AbstractApplicationServlet.service(AbstractApplicationServlet.java:528)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:729)
at
org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:292)
at
org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207)
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
at
org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:240)
at
org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207)
at
org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:212)
at
org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:106)
at
org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:502)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:141)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:79)
at
org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:616)
at
org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:88)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:522)
at
org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1095)
at
org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:672)
at
org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1500)
at
org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:1456)
at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
at
org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Unknown Source)
Where the nullpointerexception happens on the following line in
MemoryRealm.java:
validated = getCredentialHandler().matches(credentials,
principal.getPassword());
Debugging in Eclipse shows that credentialHandler is NULL here. I've
tried all combinations I can think of using the "digest" attribute on
MemoryRealm and having a nested CredentialHandler and it's always
NULL.
I don't see anything interesting in my error logs.
I have a couple questions:
1) How can I set the CredentialHandler on the MemoryRealm to avoid the
NullPointerException? I thought this would be set as a result of our
server.xml having a CredentialHandler nested inside our MemoryRealm.
>From looking at the code of RealmBase (from which MemoryRealm
inherits), I'm not even sure how it's possible to have a null
credentialHandler as it's set in startInternal, if nothing else. I
don't know what startInternal is but it sounds like something that
should be getting called.
2) Is JAASMemoryLoginModule part of a public API intended to be used
by applications? We are using it but I don't see it mentioned in the
Tomcat docs. Its javadoc says it's used "primarily for testing".
3) Is our existing configuration something that is supported in Tomcat 7/8?
4) I've been referring to the Tomcat documentation "Realm
Configuration HOW-TO". Both the 7 and 8 versions of the docs suggest
that I need to use a JAASRealm and implement my own LoginModule. Is
this preferred over the configuration I currently have?
5) Is there a better way to do what I'm doing?
Sorry for such a long email, but thanks for reading if you made it
through. Hope I'm not missing something glaringly obvious, but that's
always a distinct possibility.
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]