Custom login module
We can also write your own login module for WebSphere Application Server. You need to implement the LoginModule interface and code the following methods:
public void initialize (Subject subject, CallbackHandler callbackHandler, Map sharedState, Map options) Initialize the login module after loading. public boolean login() throws LoginException Perform the actual login. We can code the authentication check using callbacks. public boolean commit() throws LoginException After a successful login, we need to commit the login. We can insert the new subject into the security context. public boolean abort() throws LoginException In case of any problem, this method aborts the login process. public boolean logout() throws LoginException After a successful login and by the end of the session, the application needs to log out and remove the subject from the security context. Here is a simple custom login module...
package redbook.sg246316.loginmodule; import java.util.*; import javax.security.auth.*; import javax.security.auth.callback.*; import javax.security.auth.login.*; import javax.security.auth.spi.*; import redbook.sg246316.loginmodule.SamplePrincipal; public class CustomLoginModule implements LoginModule { // initial state private Subject subject; private CallbackHandler callbackHandler; private Map sharedState; private Map options; // the authentication status private boolean succeeded = false; private boolean commitSucceeded = false; // username and password private String username; private String password; // testUser's SamplePrincipal private SamplePrincipal userPrincipal; public void initialize(Subject subject, CallbackHandler callbackHandler, Map sharedState, Map options) { this.subject = subject; this.callbackHandler = callbackHandler; this.sharedState = sharedState; this.options = options; } public boolean login() throws LoginException { // prompt for a user name and password if (callbackHandler == null) throw new LoginException("Error: no CallbackHandler available!"); Callback[] callbacks = new Callback[2]; callbacks[0] = new NameCallback("user name: "); callbacks[1] = new PasswordCallback("password: ", false); try { callbackHandler.handle(callbacks); username = ((NameCallback) callbacks[0]).getName(); password = new String(((PasswordCallback) callbacks[1]).getPassword()); ((PasswordCallback) callbacks[1]).clearPassword(); } catch (java.io.IOException ioe) { throw new LoginException(ioe.toString()); } catch (UnsupportedCallbackException uce) { throw new LoginException("Error: " + uce.getCallback().toString()); } // verify the username/password // this code is using hard-coded user name and password for the sake of simplicity boolean usernameCorrect = false; boolean passwordCorrect = false; if (username.equals("testUser")) usernameCorrect = true; if (usernameCorrect && password.equals("testPassword")) { // authentication succeeded!!! passwordCorrect = true; succeeded = true; return true; } else { // authentication failed succeeded = false; username = null; password = null; throw new FailedLoginException("Authentication failed!"); } } public boolean commit() throws LoginException { if (succeeded == false) { return false; } else { // add a Principal (authenticated identity) to the Subject // this is a custom principal: SamplePrincipal // in WebSphere you may want to use the WSPrincipalImpl class userPrincipal = new SamplePrincipal(username); if (!subject.getPrincipals().contains(userPrincipal)) subject.getPrincipals().add(userPrincipal); username = null; password = null; commitSucceeded = true; return true; } } public boolean abort() throws LoginException { if (succeeded == false) { return false; } else if (succeeded == true && commitSucceeded == false) { // login succeeded but overall authentication failed succeeded = false; username = null; password = null; userPrincipal = null; } else { // overall authentication succeeded and commit succeeded, but someone else's commit failed logout(); } return true; } public boolean logout() throws LoginException { subject.getPrincipals().remove(userPrincipal); succeeded = false; succeeded = commitSucceeded; username = null; password = null; userPrincipal = null; return true; } }You will find that the custom login module uses a custom principal (SamplePrincipal). For more information about the principals, refer to the next section.
The login module basically performs the following steps:
- Initializes the login module, instantiates the necessary objects.
- Sets up the callback handler and the callback methods.
- Walks through the callbacks one after the other.
- Authenticates the user using the information returned from the callbacks.
a. If authentication is successful, it creates a principal based on the authentication data and inserts it into the subject setup during initialization. b. If authentication fails, the module destroys the objects and returns with a failed flag.
- In the meantime, the login process can be aborted.
- After a successful login, the application can also log out. The logout method should take care of removing the principal from the subject and destroying the objects in the login module.