--- a/java/org/apache/catalina/authenticator/AuthenticatorBase.java +++ a/java/org/apache/catalina/authenticator/AuthenticatorBase.java @@ -51,7 +51,6 @@ import org.apache.catalina.Session; import org.apache.catalina.TomcatPrincipal; import org.apache.catalina.Valve; -import org.apache.catalina.authenticator.jaspic.CallbackHandlerImpl; import org.apache.catalina.authenticator.jaspic.MessageInfoImpl; import org.apache.catalina.connector.Request; import org.apache.catalina.connector.Response; @@ -220,7 +219,7 @@ * default {@link org.apache.catalina.authenticator.jaspic.CallbackHandlerImpl} * will be used. */ - protected String jaspicCallbackHandlerClass = null; + protected String jaspicCallbackHandlerClass = "org.apache.catalina.authenticator.jaspic.CallbackHandlerImpl"; /** * Should the auth information (remote user and auth type) be returned as response @@ -247,6 +246,7 @@ private volatile String jaspicAppContextID = null; private volatile Optional jaspicProvider = null; + private volatile CallbackHandler jaspicCallbackHandler = null; // ------------------------------------------------------------- Properties @@ -773,7 +773,7 @@ new MessageInfoImpl(request.getRequest(), response.getResponse(), authMandatory); try { - CallbackHandler callbackHandler = createCallbackHandler(); + CallbackHandler callbackHandler = getCallbackHandler(); ServerAuthConfig serverAuthConfig = jaspicProvider.getServerAuthConfig( "HttpServlet", jaspicAppContextID, callbackHandler); String authContextID = serverAuthConfig.getAuthContextID(jaspicState.messageInfo); @@ -787,29 +787,40 @@ return jaspicState; } + private CallbackHandler getCallbackHandler() { + CallbackHandler handler = jaspicCallbackHandler; + if (handler == null) { + handler = createCallbackHandler(); + } + return handler; + } + private CallbackHandler createCallbackHandler() { CallbackHandler callbackHandler = null; - if (jaspicCallbackHandlerClass == null) { - callbackHandler = CallbackHandlerImpl.getInstance(); - } else { - Class clazz = null; - try { - clazz = Class.forName(jaspicCallbackHandlerClass, true, - Thread.currentThread().getContextClassLoader()); - } catch (ClassNotFoundException e) { - // Proceed with the retry below - } - - try { - if (clazz == null) { - clazz = Class.forName(jaspicCallbackHandlerClass); - } - callbackHandler = (CallbackHandler)clazz.getConstructor().newInstance(); - } catch (ReflectiveOperationException e) { - throw new SecurityException(e); - } + Class clazz = null; + try { + clazz = Class.forName(jaspicCallbackHandlerClass, true, + Thread.currentThread().getContextClassLoader()); + } catch (ClassNotFoundException e) { + // Proceed with the retry below } + try { + if (clazz == null) { + clazz = Class.forName(jaspicCallbackHandlerClass); + } + try { + callbackHandler = (CallbackHandler) clazz.getConstructor(Context.class) + .newInstance(); + } catch (NoSuchMethodException e) { + // No constructor with context parameter found - use no argument constructor + callbackHandler = (CallbackHandler) clazz.getConstructor().newInstance(); + } + } catch (ReflectiveOperationException e) { + throw new SecurityException(e); + } + + jaspicCallbackHandler = callbackHandler; return callbackHandler; } @@ -1284,7 +1295,7 @@ ServerAuthContext serverAuthContext; try { ServerAuthConfig serverAuthConfig = provider.getServerAuthConfig("HttpServlet", - jaspicAppContextID, CallbackHandlerImpl.getInstance()); + jaspicAppContextID, getCallbackHandler()); String authContextID = serverAuthConfig.getAuthContextID(messageInfo); serverAuthContext = serverAuthConfig.getAuthContext(authContextID, null, null); serverAuthContext.cleanSubject(messageInfo, client); --- a/java/org/apache/catalina/authenticator/jaspic/CallbackHandlerImpl.java +++ a/java/org/apache/catalina/authenticator/jaspic/CallbackHandlerImpl.java @@ -29,7 +29,9 @@ import jakarta.security.auth.message.callback.CallerPrincipalCallback; import jakarta.security.auth.message.callback.GroupPrincipalCallback; +import jakarta.security.auth.message.callback.PasswordValidationCallback; +import org.apache.catalina.Context; import org.apache.catalina.realm.GenericPrincipal; import org.apache.juli.logging.Log; import org.apache.juli.logging.LogFactory; @@ -40,23 +42,15 @@ */ public class CallbackHandlerImpl implements CallbackHandler { - private static final StringManager sm = StringManager.getManager(CallbackHandlerImpl.class); + private final Log log = LogFactory.getLog(CallbackHandlerImpl.class); // must not be static - private static CallbackHandler instance; + protected static final StringManager sm = StringManager.getManager(CallbackHandlerImpl.class); + + protected final Context context; - static { - instance = new CallbackHandlerImpl(); - } - - - public static CallbackHandler getInstance() { - return instance; - } - - - private CallbackHandlerImpl() { - // Hide default constructor + public CallbackHandlerImpl(Context context) { + this.context = context; } @@ -81,10 +75,16 @@ } else if (callback instanceof GroupPrincipalCallback) { GroupPrincipalCallback gpc = (GroupPrincipalCallback) callback; groups = gpc.getGroups(); + } else if (callback instanceof PasswordValidationCallback) { + if (context.getRealm() == null) { + log.warn(sm.getString("callbackHandlerImpl.realmMissing", + callback.getClass().getName(), context.getName())); + } else { + PasswordValidationCallback pvc = (PasswordValidationCallback) callback; + principal = context.getRealm().authenticate(pvc.getUsername(), + String.valueOf(pvc.getPassword())); + } } else { - // This is a singleton so need to get correct Logger for - // current TCCL - Log log = LogFactory.getLog(CallbackHandlerImpl.class); log.error(sm.getString("callbackHandlerImpl.jaspicCallbackMissing", callback.getClass().getName())); } --- a/java/org/apache/catalina/authenticator/jaspic/LocalStrings.properties +++ a/java/org/apache/catalina/authenticator/jaspic/LocalStrings.properties @@ -20,6 +20,7 @@ authConfigFactoryImpl.zeroLengthMessageLayer=A zero length message layer name is not valid callbackHandlerImpl.jaspicCallbackMissing=Unsupported JASPIC callback of type [{0}] received which was ignored +callbackHandlerImpl.realmMissing=Missing realm for callback of type [{0}] in context [{1}] which was ignored jaspicAuthenticator.authenticate=Authenticating request for [{0}] via JASPIC