Create a class that implements the javax.security.auth.message.module.ServerAuthModule
interface. The ServerAuthModule implementation class
must define the initialize, validateRequest, and secureResponse public
methods:
import javax.security.auth.Subject;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.message.AuthException;
import javax.security.auth.message.AuthStatus;
import javax.security.auth.message.MessageInfo;
import javax.security.auth.message.MessagePolicy;
import javax.security.auth.message.module.ServerAuthModule;
public class SampleAuthModule implements ServerAuthModule {
public void initialize(MessagePolicy requestPolicy, MessagePolicy responsePolicy, CallbackHandler handler, Map options)
throws AuthException {
...
}
public AuthStatus validateRequest(MessageInfo messageInfo, Subject clientSubject, Subject serviceSubject)
throws AuthException {
...
}
public AuthStatus secureResponse(MessageInfo messageInfo, Subject serviceSubject)
throws AuthException {
...
}
}
The initialize method in the
ServerAuthModule implementation class is called by the ServerAuthContext
implementation class to initialize the authentication module and to
associate it with the ServerAuthContext instance.
The validateRequest
and secureResponse methods in this class are used respectively to
authenticate the javax.servlet.http.HttpServletRequest and javax.servlet.http.HttpServletResponse
contained in the javax.security.auth.message.MessageInfo that is received.
These methods can use the CallbackHandler instance received in the
initialize method
to interact with the WebSphere security runtime to validate a user
password, and the active user registry to retrieve a unique-id and
membership groups for a user. The retrieved data is placed in a Hashtable
in the set of private credentials in the client subject. The WebSphere Application Server
implementation of CallbackHandler supports three callbacks:
- CallerPrincipalCallback
- GroupPrincipalCallback
- PasswordValidationCallback
WebSphere Application Server expects
the name values obtained with PasswordValidationCallback.getUsername()
and CallerPrincipalCallback.getName() to be identical. If they are
not, unpredictable results occur. The CallbackHandler's handle() method
processes each callback given in the argument array of the method
sequentially. Therefore, the name value set in the private credentials
of the client subject is the one obtained from the last callback processed.
Best practice: Always use PasswordValidationCallback to validate
a user password and to add the appropriate credentials to the client
subject during authentication:
import javax.security.auth.Subject;
import javax.security.auth.callback.Callback;
import javax.security.auth.message.AuthException;
import javax.security.auth.message.AuthStatus;
import javax.security.auth.message.MessageInfo;
import javax.security.auth.message.callback.PasswordValidationCallback;
public AuthStatus validateRequest(MessageInfo messageInfo, Subject clientSubject, Subject serviceSubject)
throws AuthException {
...
PasswordValidationCallback pvcb = new PasswordValidationCallback(clientSubject, username, password);
handler.handle(new Callback[] {pvcb});
...
}
bprac
If CallbackHandler is not used by the authentication
module, and validateRequest returns a successful status,
WebSphere Application Server requires that
a Hashtable instance be included in the clientSubject with user identity
information so that a custom login can be performed to obtain the
credentials for the user. This Hashtable can be added to the client
subject as in the following example:
import java.util.Hashtable;
import java.util.String;
import javax.security.auth.Subject;
import javax.security.auth.message.AuthException;
import javax.security.auth.message.AuthStatus;
import javax.security.auth.message.MessageInfo;
import com.ibm.wsspi.security.registry.RegistryHelper;
import com.ibm.wsspi.security.token.AttributeNameConstants.AttributeNameConstants;
public AuthStatus validateRequest(MessageInfo messageInfo, Subject clientSubject, Subject serviceSubject)
throws AuthException {
...
UserRegistry reg = RegistryHelper.getUserRegistry(null);
String uniqueid = reg.getUniqueUserID(username);
Hashtable hashtable = new Hashtable();
hashtable.put(AttributeNameConstants.WSCREDENTIAL_UNIQUEID, uniqueid);
hashtable.put(AttributeNameConstants.WSCREDENTIAL_SECURITYNAME, username);
hashtable.put(AttributeNameConstants.WSCREDENTIAL_PASSWORD, password);
hashtable.put(AttributeNameConstants.WSCREDENTIAL_GROUPS, groupList); //optional
clientSubject.getPrivateCredentials().add(hashtable);
...
}
For more information about the Hashtable requirements
and custom login, read about Developing custom login modules for a
system login configuration for JAAS.
To support the login and
authenticate methods of the Java Servlet
3.0 specification, the following logic must be added to the validateRequest
method in the ServerAuthModule implementation class:
import java.util.Map;
import javax.security.auth.Subject;
import javax.security.auth.message.AuthException;
import javax.security.auth.message.AuthStatus;
import javax.security.auth.message.MessageInfo;
import javax.servlet.http.HttpServletRequest;
public AuthStatus validateRequest(MessageInfo messageInfo, Subject clientSubject, Subject serviceSubject)
throws AuthException {
...
Map msgMap = messageInfo.getMap();
if ("login".equalsIgnoreCase(msgMap.get("com.ibm.websphere.jaspi.request"))) {
// This request is for the login method
String username = msgMap.get("com.ibm.websphere.jaspi.user");
String password = msgMap.get("com.ibm.websphere.jaspi.password");
// Authenticate using the user name and password set above.
}
else if ("authenticate".equalsIgnoreCase(msgMap.get("com.ibm.websphere.jaspi.request"))) {
// this request is for the authenticate method
String authHeader
= ((HttpServletRequest) messageInfo.getRequestMessage()).getHeader("Authorization");
if (authHeader == null) {
// The user has not provided a username and password yet, return
// AuthStatus.SEND_CONTINUE to challenge
}
else {
// Authenticate using the user name and password in the authentication header.
}
}
else {
// This is not a Servlet 3.0 login or authenticate request; handle as usual.
}
...
}