 | Level: Intermediate Hyen-Vui (Henry) Chung (hychung@us.ibm.com), Senior Software Engineer, IBM Simon Kapadia (simon.kapadia@uk.ibm.com), Security Lead, ISSW EMEA, IBM
30 Apr 2008 This series describes how to generate custom tokens using Web
services security, authenticate them with WebSphere® Application Server, and
create credentials from them. Part 2 describes the implementation and configuration
steps required to enable consumption of the custom token you generated in Part 1.
Introduction
In
Part
1
of this series, you learned how to generate and propagate custom tokens. In Part 2
you'll learn how to consume those custom tokens. The custom token used in this
article is XML-based, as in Part 1, but you can use the same process for other
token types.
The programming model is based on the pluggable token framework introduced in the
Web services security engine in WebSphere Application Server V6. We’ll use
Rational Application Developer V6.1 with WebSphere global security enabled/
The scenario
This scenario we'll use is the second part of the scenario described in
Part
1.
To recap, Listing 1 shows the XML-based custom token we used, with the sample XML
token format highlighted in bold.
Listing 1. Sample XML token format
<soapenv:Envelope xmlns:soapenv="……" >
<soapenv:Header>
<wsse:Security xmlns:wsse="……" soapenv:mustUnderstand="1">
<a:MyToken xmlns:a="http://www.acme.com" >
<a:EMail>joe.smith@acme.com</a:EMail>
</a:MyToken>
</wsse:Security>
</soapenv:Header>
<soapenv:Body>
……
</soapenv:Body>
</soapenv:Envelope>
|
In Part 2, you'll learn about the components and implementation and configuration
steps required to enable a Web service to consume this custom token, to map the
identity to a user in user registry, and to create the WebSphere credential, which
can be used for J2EE authorization checks.
Receiver components
In order for the Web services security run-time to understand a custom token, you
need to create a number of distinct components. Each of these is a separate
Java™
class, and they work together to parse the token, authenticate the identity in the
token, and create the relevant credentials. Each of the components has a distinct
responsibility. Figure 1 shows the necessary components.
Figure 1. Receiver-side components
The components (highlighted in red) that you need to implement to consume the
custom XML token MyToken are:
-
MyTokenConsumer, which implements the token consumer
interface, and is responsible for parsing the XML representation of the security
token from the SOAP message. It calls the Java Authentication and Authorization
Service (JAAS) Login Module via the JAAS programming model to validate the
security token.
-
MyLoginModule, which implements the JAAS Login Module
interface, and is responsible for validating the security token obtained from the
callback handler and creating the relevant credentials if the authentication
succeeds.
-
MyCallbackHandler, which implements the JAAS callback
handler interface, and is responsible for supplying security information to the JAAS
Login Module.
-
MyToken, which extends the WSSToken abstract class,
and
is a Java object that represents the external token. This is distinct from
(although related to) the external token.
The interactions of these components are explained in
Develop the components.
Overview of the process
To configure the Web Services security run-time to authenticate the custom
tokens, you need to complete the following steps, which are described in detail in
this article:
- Develop the required code components described in the previous section, and
deploy them to the application server.
- Create a JAAS Login Configuration alias with a JAAS login module for the
token consumer to use.
- Create a custom token configuration in the application deployment descriptor
and bindings using the Web Services editor in the development tooling (for
instance in Rational Application Developer 6.1 or the Application Server
Toolkit)
- Deploy the Web services application to the application server.
The result is a Web service configured to accept a custom token and use it for
authentication.
Configure the
Web services application to consume the custom token
describes the detailed steps using Rational Application Developer.
Develop the components
This section describes how to create the components required to consume the XML
custom token.
Implement the token consumer
The token consumer MyTokenConsumer is responsible for
parsing the XML representation of the security token from the SOAP message. The
XML representation of the security token is passed in the
invoke method as an XML
Element parameter. In our sample, the implementation
parses the XML token and passes the token information using the JAAS callback
handler (MyCallbackHandler) to the JAAS Login Module
(MyLoginModule) for token validation and mapping. The
implementations of the callback handler and the JAAS Login Module are described in
the following sections.
MyTokenConsumer must implement the
com.ibm.wsspi.wssecurity.token.TokenConsumerComponent
Java interface. The key method is the invoke method,
which is where the majority of the implementation is.
Listing 2. Implementation of MyTokenConsumer.invoke method
public void invoke(Node target, final Map context) throws
SoapSecurityException {
……
final TokenConsumerConfig config =
(TokenConsumerConfig) context.get(TokenConsumerConfig.CONFIG_KEY);
// get the SOAP MessageContext
SOAPMessageContext messageContext = (SOAPMessageContext)
context.get(com.ibm.wsspi.wssecurity.Constants.
WSSECURITY_MESSAGE_CONTEXT);
if(target != null) {
if (target.getNodeType() == Node.ELEMENT_NODE) {
// parse the XML token from SOAP Message
Element tokenElement = Util.clone((Element) target);
String idattr = Util.getIdAttribueName(tokenElement);
String tokenId = null;
if (idattr != null) {
tokenId = tokenElement.getAttribute(idattr);
}
Element emailElem = Util.getOneElement(tokenElement,
Util.MYTOKEN_NS, "EMail");
String email = Util.getStringValue(emailElem);
final String jaasLC = config.getJAASConfig();
……
final Map properties = new HashMap(); // could pass extra properties
properties.put(Util.MSG_CONTEXT, messageContext);
properties.put(Util.TOKEN_ID, tokenId);
properties.put(Util.TOKEN_XML_ELEMENT, tokenElement);
final MyCallbackHandler ch = new MyCallbackHandler
(email, properties);
MyToken t = null;
try {
t = AccessController.doPrivileged(new
PrivilegedExceptionAction<MyToken>() {
public MyToken run() throws LoginException {
Subject subject = (Subject)
context.get
(com.ibm.wsspi.wssecurity.Constants.WSSECURITY_SUBJECT);
LoginContext lc = new LoginContext(jaasLC, subject, ch);
lc.login();
// Making sure we get back the token created by this
// Login Module
MyToken token = (MyToken)
properties.get
(com.ibm.wsspi.wssecurity.Constants.WSSECURITY_TOKEN_LOGININFO);
token.setUsedTokenConsumer(config);
// set the token consumer configuration used
return token;
}
});
System.out.println(quot;Token created: quot; + t.toString());
} catch (PrivilegedActionException pae) {
……
}
} else {
throw new SoapSecurityException(……
}
} else {
throw new SoapSecurityException(……
}
}
|
The XML token is passed as a DOM Element in the
invoke method, with a parameter name of
target. The context object
is the current run-time context, which is a hash map and contains contextual information like:
-
SOAPMessageContext (SOAP message context), which is obtained by using
com.ibm.wsspi.wssecurity.Constants.WSSECURITY_MESSAGE_CONTEXT
as key from the context.
-
TokenConsumerConfig (Token consumer configuration), which is obtained by using
TokenConsumerConfig.CONFIG_KEY as key from the
context.
-
Subject (the temporary JAAS subject), which is the container for all security tokens
processed in the current request. The contents are used to create the WebSphere
JAAS Caller and Invocation Subjects. It is obtained by using
com.ibm.wsspi.wssecurity.Constants.WSSECURITY_SUBJECT
as key from the context.
 | We recommend that you create a clone of the security token XML element to store
in the WSSToken object. Otherwise, the whole SOAP
message is referenced in memory, which could cause memory problems for large SOAP
messages. The sample provides an empty implementation in the
Util.clone method, and leaves it to users to provide an
implementation. |
|
After the token is parsed,
MyTokenConsumer reads the JAAS Login Configuration name
from the TokenConsumerConfig object. The
TokenConsumerConfig is populated with the configuration
data from the deployment descriptor and bindings (described in
Configure the
Web services application to consume the custom token).
You create the JAAS Login Configuration name using the administration console,
also described in that section, and should contain the
MyLoginModule JAAS Login Module.
MyTokenConsumer invokes
MyLoginModule using the JAAS APIs. The token
information is passed to MyLoginModule using
MyCallbackHandler.
If the authentication is successful, MyTokenConsumer
sets the TokenConsumerConfig to
MyToken using the
setUsedTokenConsumer API. The
TokenConsumerConfig is used in the run-time to identify
which token consumer configuration to use to process the token.
Note that you should use SoapSecurityException to
communicate any failure to the Web services security run-time.
Implement the JAAS Login
Module
The JAAS Login Module is responsible for validating the security token,
authenticating the token, and identity mapping. In our example,
MyLoginModule performs a simple mapping of the email
address to an identity in the user registry.
MyLoginModule must implement the JAAS
javax.security.auth.spi.LoginModule interface. The two
key methods of the JAAS Login Module are the login and
commit methods. The login
method is where authentication, validation, and identity mapping should occur and
the commit method creates the security credential and
sets it to the subject. For detailed information about the life cycle of a JAAS
Login Module, see
Interface
LoginModule
in the JDK 5.0 Documentation.
Listing 3 shows the implementation of the
MyLoginModule.login method
Listing 3. Implementation of MyLoginModule.login method
public boolean login() throws LoginException {
MyEmailCallback ec = new MyEmailCallback();
PropertyCallback pc = new PropertyCallback(null);
Callback[] callbacks = new Callback[] { ec, pc };
try {
this._ch.handle(callbacks);
} catch (UnsupportedCallbackException e) {
……
}
// Perform the authentication, here is just a simple mapping
this._email = ec.getEmail();
if (this._email == null || this._email.length() == 0) {
throw new LoginException("No email found!");
}
int i = this._email.indexOf('@');
if (i == -1) {
throw new LoginException(this._email + " is not a valid email");
}
this._username = this._email.substring(0, i);
this._properties = pc.getProperties();
if (this._properties != null) {
this._tokenId = (String) this._properties.get(Util.TOKEN_ID);
this._tokenElement = (Element) this._properties.get
(Util.TOKEN_XML_ELEMENT);
}
this._loginSuccess = true;
return this._loginSuccess;
}
|
This method creates an array of callback objects and passes them to the
CallbackHandler.handle method. The callback objects are
MyEmailCallback and
PropertyCallback. The callback handler supplies the
information through the callback objects to the JAAS Login Module, as described in
Implement the JAAS Callback Handler.
After the JAAS Login Module obtains the information, it can perform
authentication and validation of the token information. In our sample, an identity
mapping is performed on the email by stripping of the string after
@,
and saved it as this._username, which is the principal used in constructing
the MyToken security token. The returned principal
(MyToken.getPrincipal) is then used to map it to a user identity in
the user registry in the Caller processing.
y. The login method saves
the authenticated data and makes it available to the
commit method.
Listing 4 shows the implementation of the
MyLoginModule.commit method
Listing 4. Implementation of MyLoginModule.commit method
public boolean commit() throws LoginException {
boolean ret = false;
if (this._loginSuccess) {
MyToken t = new MyToken(this._email, this._tokenId, this._username,
this._tokenElement);
if (this._properties == null) {
System.out.println("WARNING: Can't return MyToken because
properties parameter is null.");
} else {
this._properties.put
(com.ibm.wsspi.wssecurity.Constants.WSSECURITY_TOKEN_LOGININFO, t);
}
this._subject.getPrivateCredentials().add(t);
ret = true;
}
return ret;
}
|
The commit method then checks whether log-in is
successful, and creates the security token MyToken and
adds it to the Subject. This implementation also puts
the instance of MyToken into the
properties, so that
MyTokenConsumer can get to the specific instance. This
is useful when you have multiple instances of MyToken.
Implement the JAAS Callback Handler
The callback handler acts as a channel to pass data between
MyTokenConsumer and
MyLoginModule, leveraging the JAAS programming model.
The data container for the data is the callback. The two callbacks used in this
sample are:
-
MyEmailCallback for passing the email
-
PropertyCallback for passing the contextual data in
the map
The implementation of MyCallbackHandler is very
simple. Listing 5 shows the is the handle method:
Listing 5. Implementation of MyCallbackHandler method
public void handle(Callback[] callbacks) throws IOException,
UnsupportedCallbackException {
for (int i = 0; i < callbacks.length; i++) {
Callback c = callbacks[i];
if (c instanceof MyEmailCallback) {
((MyEmailCallback) c).setEmail(this._email);
} else if (c instanceof PropertyCallback) {
((PropertyCallback) c).setProperties(this._properties);
} else {
System.out.println("WARNING: unsupported callback " +
c.getClass().getName());
}
}
}
|
This implementation passes the data from
MyTokenConsumer to
MyLoginModule.
Implement the security token
(WSSToken)
The MyToken security token is the Java representation
of the custom token. It must extend the
com.ibm.wsspi.wssecurity.auth.token.WSSToken abstract
class. The MyToken object is the Java representation of
the XML-based security token. In this example, it is the same for both the
sender’s and the receiver’s side. It holds the email address and the mapped
principal. The source code for the MyToken
implementation is included in
Part
1.
The following are the key methods:
-
Constructor sets the qualified name
(QName) of the token type using the
setType() method. This is how the token is identified
in the run-time. In this sample, the token type is
{http://www.acme.com}MyToken. Note that this is the
type used in
Configure the
Web services application to consume the custom token,
when defining the custom token in the deployment descriptor and bindings.
-
getEmail returns the email address. This method is
specific to the XML email token used in this sample.
-
getExipration returns the expiration time (in
milliseconds). In this sample, there is no expiration for
MyToken, so it's set to
java.lang.Long.MAX_VALUE.
-
getPrincipal returns the principal name of the
security token. In our sample, that's the user name of the email address. This
is the identity name used by the Web services security run-time to create the
WebSphere credential, which is described in
Create the
caller.
-
getUniqueID returns the unique ID that will be
included as a part of the cache key used in the authenticated
Subject. This is used by the receiver side. In our
sample, it uses the hash code of MyToken. If the
token does not want to affect the cache key, the implementation should return
null.
-
isValid returns the validity of the token. In this
example, the check is very simple; the token is valid if the email is present
and not null.
 |
Configure the Web services
application to consume the custom token
Now that we've created the run-time components for consuming a custom token, the
next step is to configure the Web services application to consume the
XML-based security token by doing the following:
- Create the JAAS login configuration with
MyLoginModule.
- Create a required security token of type
{http://www.acme.com}MyToken in the
Extension of the Web services provider.
- Create a caller of type
{http://www.acme.com}MyToken in the
Extension of the Web services provider.
- Create a token consumer for the MyTokenConsumer implementation in the
Bindings of the Web services provider.
This following sections walk you through these configuration steps.
Note: Make sure that WebSphere global security is enabled and that the user
registry contains a user called joe.smith.
Create the JAAS login
configuration
In order for MyTokenConsumer to use
MyLoginModule, you need to create a new JAAS log-in
configuration using the WebSphere administrative console.
- In the administrative console, select Security => Secure
administration, applications, and infrastructure, as shown in Figure 2.
Figure 2. Select Secure
administration, applications, and infrastructure
- In the Secure administration, applications, and infrastructure dialog, select
Java Authentication and Authorization Service, and then Application
logins
- Click New to create a new application login configuration, as shown in
Figure 3.
Figure 3. Create new
application login configuration
- Specify
acme.mytoken in the Alias field and
click OK, as shown in Figure 4.
Figure 4. Configuration
properties
- Select acme.mytoken in the list of application aliases, as shown in
Figure 5.
Figure 5. Select new alias
- Select JAAS login modules under Additional properties, as shown
in Figure 6.
Figure 6. Select JAAS login
modules
- Click New, as shown in Figure 7.
Figure 7. Click New to create
a new login module
- In the Module class name field, type the class name of the login
module, which is
com.acme.security.MyLoginModule for
this sample. Make sure that REQUIRED is selected for Authentication
strategy, then click OK.
Figure 8. Specify module
properties
- Save the configuration by clicking the Save link at the top of the
screen.
The new JAAS Login Configuration acme.mytoken is now
available for use in the Token Consumer binding configuration.
Create the security token
To create the security token, complete the following steps:
- In the Project Explorer, open the Web services editor by double-clicking
webservices.xml, as shown in Figure 9.
Figure 9. Open the Web
services editor
- Select the Extensions tab at the bottom of the screen, as shown in
Figure 10.
Figure 10. Select Extensions
tab
- Expand Request Consumer Service Configuration Details, then expand
Required Security Token, then click Add, as shown in Figure 11.
Figure 11. Add required
security token
- In the Required Security Token dialog, enter a name for the token type, a
token URI, and a local name to match token type
{http://www.acme.com}MyToken, as shown in Figure 12.
Make make sure Required is selected for Usage type, and click
OK.
Figure 12. Specify security
token information
The Web services security run-time will now ensure that a security token of type
{http://www.acme.com}MyToken is sent in the SOAP
message.
Create the caller
The next step is to create the caller configuration. The caller configuration
defines which token type is used by the WebSphere security run-time to create the
WebSphere credentials.
- Expand Request Consumer Service Configuration Details, then expand
Caller Part, and click Add, as shown in Figure 13.
Figure 13. Add caller
- In the Caller part dialog, specify a name, a token URI, and a local name to
match token type
{http://www.acme.com}MyToken, as
shown in Figure 14.
Figure 14. Specify caller
information
The run-time for Web services security uses this token identity to create the
security credential and principal for the WebSphere Application Server security
run-time. The token identity must be in the configured user registry. The identity
of this token can be used for J2EE role-based authorization checks or other
security services in the application server.
Create the token consumer
Now you need to create a token consumer for
MyTokenConsumer.
- Open the Web services editor as described earlier, and click the Binding
Configuration tab at the bottom of the screen:
Figure 15. Select Binding
Configuration tab
- Expand Request Consumer Binding Configuration Details, then Token
Consumer, and click Add, as shown in Figure 16.
Figure 16. Create token
consumer
- In the Token Consumer dialog, shown in Figure 17, specify the following
information:
- Enter a name in the Token consumer name field.
- Specify the token consumer implementation class in the Token consumer
class field. For this example, use
com.acme.security.MyTokenConsumer.
- For the Security token, select the name specified in the Security
Token dialog (in this case,
myToken).
- Check Use value type.
- Specify the token URI and local name to match token type
{http://www.acme.com}MyToken.
- Specify
acme.mytoken for jaas.config
name.
Figure 17. Specify security
token information
Deploy the application and
run the client
Finally, you can deploy the application and run the client as described in
Part
1,
by completing the following steps:
- Deploy CustomTokenService.ear and start the application, by entering the
following command:
wsadmin>$AdminApp install "/tmp/CustomTokenService.ear"
{-usedefaultbindings -appname CustomTokenService -verbose}
wsadmin>$AdminConfig save
wsadmin>set appMgr [$AdminControl queryNames cell=……,node=……,
type=ApplicationManager,process=server1,*]
wsadmin>$AdminControl invoke $appMgr startApplication CustomTokenService
|
- Start TCPMON and set the listening port to 9082 by entering the following:
java -cp $WAS_HOME/runtimes/com.ibm.ws.webservices.thinclient_6.1.0.jar
com.ibm.ws.webservices.engine.utils.tcpmon 9082 localhost 9080
|
- Export the client application from Rational Application Developer by entering
the following command (assume the client application name is
CustomTokenClient.ear):
launchClient.sh CustomTokenClient.ear
|
The following message is captured by TCPMON when running the sample
Listing 6. Captured SOAP message from the sample
<soapenv:Envelope
xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soapenv:Header>
<wsse:Security
xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/
oasis-200401-wss-wssecurity-secext-1.0.xsd"
soapenv:mustUnderstand="1">
<a:MyToken xmlns:a="http://www.acme.com"
xmlns:u="http://docs.oasis-open.org/wss/2004/01/
oasis-200401-wss-wssecurity-utility-1.0.xsd"
u:Id="com.acme.security.MyTokenGenerator_1">
<a:EMail>joe.smith@acme.com</a:EMail>
</a:MyToken>
</wsse:Security>
</soapenv:Header>
<soapenv:Body>
<p657:doEcho xmlns:p657="http://www.acme.org">
<s>Custom Token Test</s>
</p657:doEcho>
</soapenv:Body>
</soapenv:Envelope>
|
In the SystemOut.log, check the following trace output to verify that the email
XML token is consumed by the Web services provider and a WebSphere Caller Subject
is created based on the email mapped identity and available by calling
com.ibm.websphere.security.auth.WSSubject.getCallerSubject().
Listing 7 shows the output trace. The bold text is the output from the
MyToken.toString method.
Listing 7. Output trace
[12/5/07 ……] 00000021 SystemOut O Token created:
com.acme.security.MyToken[tokenId=com.acme.security.MyTokenGenerator_1],
element=[com.ibm.ws.webservices.engine.xmlsoap.SOAPElement ---
{http://www.acme.com}MyToken], username=[joe.smith],
email=[joe.smith@acme.com]
[12/5/07 ……] 00000021 SystemOut O Subject: Subject:
Principal: customRealm/joe.smith
Public Credential:
com.ibm.ws.security.auth.WSCredentialImpl@69866986
Private Credential:
com.acme.security.MyToken[tokenId=com.acme.security.MyTokenGenerator_1],
element=[com.ibm.ws.webservices.engine.xmlsoap.SOAPElement ---
{http://www.acme.com}MyToken], username=[joe.smith], email=[joe.smith@acme.com]
Private Credential:
com.ibm.ws.security.token.SingleSignonTokenImpl@26322632
Private Credential:
com.ibm.ws.security.token.AuthenticationTokenImpl@147e147e
Private Credential:
com.ibm.ws.security.token.AuthorizationTokenImpl@25582558
|
Summary
This concludes our two-part series on propagating and consuming custom XML-based
security tokens using the OASIS Web Services Security SOAP Message Security 1.0
specification. Although the sample we used is very simple, it demonstrates all the
necessary steps to enable a Web service client that you can use to propagate a custom
token, and to enable a Web service provider to consume the custom token. You can apply
these concepts to other token types, such as a binary security tokens.
In this series, we walked through the steps to implement a custom token using the
SPI interfaces from the pluggable security token framework, as well as the
configuration steps to enable propagation of that custom token in an application
client and to enable a Web services provider to consume the custom token.
Download | Description | Name | Size | Download method |
|---|
| Sample project and code | part2sample.zip | 18KB | HTTP |
|---|
Resources
About the authors  | 
|  |
Henry Chung is an architect and lead developer of Web services security on the WebSphere platform. Henry has been in middleware development for over 5 years and has developed many security features for the WebSphere platform. Henry&s current focus is leading the development of the latest WebSphere Web services security specifications. He also helps customers and other IBM teams apply Web services security solutions. His primary goal is to enable WebSphere Web services security support to meet real-world needs. You can contact Henry at hychung@us.ibm.com.
|
 | 
|  | Simon Kapadia is the Technical Team Lead for IBM Software Services for WebSphere (ISSW) in the United Kingdom. He works on designing and implementing large distributed computer systems for IBM customers. Simon holds a Bachelor’s degree in English and Drama, and a Master’s in Computing Science. He has owned a computer since six years of age, and has turned a lifelong hobby into a career. Prior to joining IBM, Simon developed software for digital exchanges at Bell Laboratories, managed networks and Web applications at a Polish ISP, and supported and consulted on DCE, DFS and Encina for Transarc Corporation. You can reach Simon at simon_kapadia@uk.ibm.com or via his public Web site at http://kapadia.pl. |
Rate this page
|  |