You can develop a custom Java™ Authentication
SPI for Containers (JASPIC) authentication provider by creating classes
that implement the required interfaces noted in the JSR 196: Java Authentication Service Provider
Interface for Containers specification.
About this task
WebSphere Application Server Liberty supports
the use of third-party authentication providers that are compliant
with the servlet container specified in Java Authentication SPI for Containers (JASPIC)
Version 1.1.
The servlet container defines interfaces
that are used by the security runtime environment in collaboration
with the web container in the WebSphere® Application
Server to invoke authentication modules before and after a web request
is processed by an application. Authentication that uses JASPIC modules
is performed only when JASPIC is enabled in the security configuration.
To
develop a custom authentication provider, create classes that implement
the required interfaces noted in the JSR 196: Java Authentication Service Provider Interface
for Containers specification. A provider can use one or more authentication
modules for authentication. Modules can use callbacks to perform authentication,
or they can manually add the necessary user identity information to
the client subject.
- Create a class that implements thejavax.security.auth.message.config.AuthConfigProvider interface.
The
AuthConfigProvider implementation class
must define a public two-argument constructor and the
getServerAuthConfig public
method:
import java.util.Map;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.message.AuthException;
import javax.security.auth.message.config.AuthConfigFactory;
import javax.security.auth.message.config.AuthConfigProvider;
import javax.security.auth.message.config.ServerAuthConfig;
public class SampleAuthConfigProvider implements AuthConfigProvider {
public SampleAuthConfigProvider(Map<String, String> properties, AuthConfigFactory factory) {
...
}
public ServerAuthConfig getServerAuthConfig(String layer, String appContext, CallbackHandler handler)
throws AuthException {
...
}
}
An instance of the AuthConfigProvider implementation
class is used by WebSphere Application
Server when a request arrives to be processed by the web module of
the application. The getServerAuthConfig method is
used to obtain a ServerAuthConfig instance. The CallbackHandler argument
in the method call is used by the authentication module.
- Create a class that implements thejavax.security.auth.message.config.ServerAuthConfig interface.
The
ServerAuthConfig implementation class
must define the
getAuthContextID and
getAuthContext public
methods:
import java.util.Map;
import javax.security.auth.Subject;
import javax.security.auth.message.AuthException;
import javax.security.auth.message.MessageInfo;
import javax.security.auth.message.config.ServerAuthConfig;
import javax.security.auth.message.config.ServerAuthContext;
public class SampleServerAuthConfig implements ServerAuthConfig {
public String getAuthContextID(MessageInfo messageInfo) throws IllegalArgumentException {
...
}
public ServerAuthContext getAuthContext(String authContextID, Subject serviceSubject, Map properties)
throws AuthException {
...
}
}
The getAuthContextID and getAuthContext methods
in the ServerAuthConfig implementation class are
used to obtain a ServerAuthContext instance.
- Create a class that implements the javax.security.auth.message.config.ServerAuthContext interface.
The
ServerAuthContext implementation class
must define the
validateRequest and
secureResponse public
methods:
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.security.auth.message.config.ServerAuthContext;
public class SampleServerAuthContext implements ServerAuthContext {
public AuthStatus validateRequest(MessageInfo messageInfo, Subject clientSubject, Subject serviceSubject)
throws AuthException {
...
}
public AuthStatus secureResponse(MessageInfo messageInfo, Subject serviceSubject)
throws AuthException {
...
}
}
The validateRequest method in
the ServerAuthContext implementation class is used
to invoke the module that authenticates the received web request message.
If the authentication result is successful, the web container dispatches
the received web request message that the target web module processes
in the application. If the authentication result is not successful,
the request is rejected with the appropriate response status.
- 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 {
...
}
public void cleanSubject(MessageInfo messageInfo, Subject subject)
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 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
that is received in the
initialize method to interact
with the WebSphere security
run time to validate a user password, and the active user registry
to retrieve a unique id and group membership 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 the
CallbackHandler supports the following 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 handle() method
of the CallbackHandler processes each callback that
is 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.
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, see Developing JAAS custom login modules
for a system login configuration.