Security Service
1 Overview
The purpose is to make a simple, unified way for the authentication and the storing/propagation of user sessions through all the eXo components and J2EE containers. JAAS is supposed to be the primary login mechanism but the Security Service framework should not prevent other (custom or standard) mechanisms from being used. You can learn more about JAAS in the Java Tutorial2 Framework
The central point of this framework is the ConversationState object which stores all information about the state of the current user (very similar to the Session concept). The same ConversationState also stores acquired attributes of an Identity which is a set of principals to identify a user. The ConversationState has definite lifetime. This object should be created when the user's identity becomes known by eXo (login procedure) and destroyed when the user leaves an eXo based application (logout procedure). Using JAAS it should happen in LoginModule's login() and logout() methods respectively.2.1 ConversationState and ConversationRegistry
The ConversationState can be stored- in a static local thread variable, or
- as a key-value pair in the ConversationRegistry component.
Storing the ConversationState in a static local thread variable makes it possible to represent it as a context (current user's state).
ConversationState.setCurrent(conversationState); .... ConversationState.getCurrent();
conversationRegistry.register("key", conversationState); ... conversationRegistry.getState("key");
The ConversationRegistry is a mandatory component deployed into eXo Container as following:
<component>
<type>org.exoplatform.services.security.ConversationRegistry</type>
</component>2.2 Authenticator
An Authenticator is responsible for Identity creating, it contains two methods:- validateUser() accepts an array of credentials and returns the userId (which can be something different from the username).
- createIdentity() accepts the userId and returns a newly created Identity object.
public interface Authenticator { /** * Authenticate user and return userId which can be different to username. * @param credentials - list of users credentials (such as name/password, X509 certificate etc) * @return userId * @throws LoginException * @throws Exception */ String validateUser(Credential[] credentials) throws LoginException, Exception; /** * @param credentials - userId. * @return Identity * @throws Exception */ Identity createIdentity(String userId) throws Exception; }
Configuration example:
<component>
<key>org.exoplatform.services.security.Authenticator</key>
<type>org.exoplatform.services.organization.auth.OrganizationAuthenticatorImpl</type>
</component>3 Usage
3.1 JAAS login module
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.
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);- LoginModule.logout() can be called by JAASConversationStateListener, it extends ConversationStateListener.
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."); }
3.2 Predefinded JAAS login modules
There are several JAAS Login modules included in eXo Platform sources: org.exoplatform.services.security.jaas.DefaultLoginModule which provides both authentication (using eXo Authenticator based mechanism) and authorization, filling Conversation Registry as it described in previous section. There are also several per-Application Server extensions of this login module which can be found in org.exoplatform.services.security.jaas package, which can be used in appropriate AS. In particular, we have dedicated Login modules for Tomcat, JBoss, Jonas and WebSphere. TODO: configuration examples for Tomcat, JBoss, Jonas and WebSphere Besides that, for the case when third party authentication mechanism required, we have org.exoplatform.services.security.jaas.IdentitySetLoginModule, which catches a login identity from third party "authenticating" login module and preforms eXo specific authorization job. In this case third party login module has to put login (user) name to the shared state map under "javax.security.auth.login.name" key and third party LM has to be configured before IdentitySetLoginModule like:exo {
com.third.party.LoginModuleImpl required;
org.exoplatform.services.security.jaas.IdentitySetLoginModule required;
};3.3 J2EE container authentication
As you know, when a user in JAAS is authenticated, a Subject is created as a result. This Subject represents the authenticated user. It is important to know and follow the rules regarding Subject filling which are specific for each J2EE server, where eXo Platform is deployed. To make it workable for the particular J2EE server it is necessary to add specific Principals/Credentials to the Subject to be propagated into the specific J2EE container implementation. We extended the DefaultLoginModule by overloading its commit() method with a dedicated logic, presently available for Tomcat, JBOSS and JONAS application servers. Furthermore you can use the optional RolesExtractor which is responsible for mapping primary Subject's principals (userId and a set of groups) to J2EE Roles:public interface RolesExtractor { Set <String> extractRoles(String userId, Set<MembershipEntry> memberships); }
on 16/05/2009 at 06:51