The framework described is not coupled with any authentication mechanism but the most logical and implemented by default is the JAAS Login module. The typical sequence looks as follows (see org.exoplatform.services.security.jaas.DefaultLoginModule):
LoginModule.login() creates a list of credentials using standard JAAS Callbacks features, obtains an Authenticator instance, and creates an Identity object calling Authenticator.authenticate(..) method
Authenticator authenticator = (Authenticator) container()
.getComponentInstanceOfType(Authenticator.class);
// RolesExtractor can be null
RolesExtractor rolesExtractor = (RolesExtractor) container().
getComponentInstanceOfType(RolesExtractor.class);
Credential[] credentials = new Credential[] {new UsernameCredential(username), new PasswordCredential(password) };
String userId = authenticator.validateUser(credentials);
identity = authenticator.createIdentity(userId);
LoginModule.commit() obtains the IdentityRegistry object, and register the identity using userId as a key.
When initializing the login module, you can set the option parameter "singleLogin". With this option you can disallow the same Identity to login for a second time.
By default singleLogin is disabled, so the same identity can be registered more than one time. Parameter can be passed in this form singleLogin=yes or singleLogin=true.
IdentityRegistry identityRegistry = (IdentityRegistry) getContainer().getComponentInstanceOfType(IdentityRegistry.class);
if (singleLogin && identityRegistry.getIdentity(identity.getUserId()) != null)
throw new LoginException("User " + identity.getUserId() + " already logined.");
identity.setSubject(subject);
identityRegistry.register(identity);
In the case of using several LoginModules, JAAS allows to place the login() and commit() methods in different REQUIRED modules.
After that, the web application must use SetCurrentIdentityFilter. This filter obtains the ConversationRegistry object and tries to get the ConversationState by sessionId (HttpSession). If there is no ConversationState, then SetCurrentIdentityFilter will create a new one, register it and set it as current one using ConversationState.setCurrent(state).
LoginModule.logout() can be called by JAASConversationStateListener, it extends ConversationStateListener.
This listener must be configured in web.xml. The method sessionDestroyed(HttpSessionEvent) is called by the ServletContainer. This method removes the ConversationState from the ConversationRegistry ConversationRegistry.unregister(sesionId) and calls the method LoginModule.logout().
ConversationRegistry conversationRegistry = (ConversationRegistry) getContainer().getComponentInstanceOfType(ConversationRegistry.class);
ConversationState conversationState = conversationRegistry.unregister(sesionId);
if (conversationState != null) {
log.info("Remove conversation state " + sesionId);
if (conversationState.getAttribute(ConversationState.SUBJECT) != null) {
Subject subject = (Subject) conversationState.getAttribute(ConversationState.SUBJECT);
LoginContext ctx = new LoginContext("exo-domain", subject);
ctx.logout();
} else {
log.warn("Subject was not found in ConversationState attributes.");
}