The Generic Issued token generator and consumer, GenericIssuedTokenGenerateLoginModule,
and GenericIssuedTokenConsumeLoginModule, can be used in conjunction
with the GenericSecurityTokenFactory and GenericSecurityToken SPIs
to implement an end-to-end solution for a custom token. Generating
and consuming custom tokens with the Generic Issue Login Modules can
be done with either policy and bindings, or WSSAPIs.
Before you begin
You must have a functioning set of JAX-WS service client
and provider applications to which you can add new JAAS login module
classes.
About this task
Complete the following steps if you want to enable a set
JAX-WS service client and provider applications to use a custom token.
Within these steps MyToken is the name of the token being created.
As you complete this task:
- Two JAAS login modules are created; one to generate the token,
and one to consume it.
- The token is generated and consumed with the assistance of the
Generic Issued token consumer and generator.
- The security constraints are then applied to the applications
with policy sets and bindings.
Procedure
- Create the following generator JAAS login module and make
it available to your application code
package test.tokens;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import javax.security.auth.Subject;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.login.LoginException;
import javax.security.auth.spi.LoginModule;
import javax.xml.soap.SOAPFactory;
import javax.xml.soap.SOAPElement;
import javax.xml.namespace.QName;
import com.ibm.websphere.wssecurity.wssapi.token.GenericSecurityTokenFactory;
import com.ibm.websphere.wssecurity.wssapi.token.SecurityToken;
public class MyCustomGenerator implements LoginModule {
private Map _sharedState;
private Map _options;
private CallbackHandler _handler;
public void initialize(Subject subject, CallbackHandler callbackHandler,
Map<String, ?> sharedState, Map<String, ?> options) {
this._handler = callbackHandler;
this._sharedState = sharedState;
this._options = options;
}
public boolean login() throws LoginException {
GenericSecurityTokenFactory factory = null;
try {
factory = GenericSecurityTokenFactory.getInstance();
} catch (Exception e) {
throw new LoginException(e.toString());
}
if (factory == null) {
throw new LoginException("GenericSecurityTokenFactory.getInstance() returned null");
}
SecurityToken myToken = null;
try {
SOAPElement tokenElement = createCustomElement(factory);
myToken = factory.getToken(tokenElement, new QName("http://www.acme.com","MyToken"));
} catch (Exception e) {
throw new LoginException(e.toString());
}
if (myToken == null) {
throw new LoginException("myToken is null");
}
//Put the token in a list on the shared state where it will be available to be used by
//stacked login modules
factory.putGeneratorTokenToSharedState(_sharedState, myToken);
return true;
}
private SOAPElement createCustomElement(GenericSecurityTokenFactory gstFactory) throws Exception {
/*
<acme:MyToken xmlns:acme="http://www.acme.com"
xmlns:utl="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" utl:Id="cust_3">
<acme:EMail>joe.smith@acme.com</acme:EMail>
</acme:MyToken>
*/
SOAPFactory factory = SOAPFactory.newInstance();
//Create the MyToken element
SOAPElement tokElement = factory.createElement("MyToken", "acme", "http://www.acme.com");
//Add the Id attribute
tokElement.addAttribute(new QName("http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd", "Id", "utl"), gstFactory.createUniqueId());
//Create the Email element
SOAPElement emailElement = factory.createElement("Email", "acme", "http://www.acme.com");
emailElement.addTextNode("joe.smith@acme.com");
//Add the EMail element to the MyToken
tokElement.addChildElement(emailElement);
return tokElement;
}
public boolean logout() throws LoginException {
return false;
}
public boolean abort() throws LoginException {
return false;
}
public boolean commit() throws LoginException {
return true;
}
}
- Create the following consumer JAAS login module and make
it available to your application code
package test.tokens;
import java.util.Map;
import javax.xml.namespace.QName;
import org.apache.axiom.om.OMElement;
import javax.security.auth.Subject;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.login.LoginException;
import javax.security.auth.spi.LoginModule;
import com.ibm.websphere.wssecurity.callbackhandler.PropertyCallback;
import com.ibm.websphere.wssecurity.wssapi.token.GenericSecurityTokenFactory;
import com.ibm.websphere.wssecurity.wssapi.token.SecurityToken;
import com.ibm.wsspi.wssecurity.wssapi.OMStructure;
public class MyCustomConsumer implements LoginModule {
private CallbackHandler _handler;
private Map _sharedState;
public void initialize(Subject subject, CallbackHandler callbackHandler,
Map<String, ?> sharedState, Map<String, ?> options) {
this._handler = callbackHandler;
this._sharedState = sharedState;
}
public boolean login() throws LoginException {
PropertyCallback propertyCallback = new PropertyCallback(null);
Callback[] callbacks = new Callback[] { propertyCallback};
try {
this._handler.handle(callbacks);
} catch (Exception e) {
throw new LoginException(e.toString());
}
//Get the GenericSecurityTokenFactory
GenericSecurityTokenFactory factory = null;
try {
factory = GenericSecurityTokenFactory.getInstance();
} catch (Exception e) {
throw new LoginException(e.toString());
}
if (factory == null) {
throw new LoginException("GenericSecurityTokenFactory.getInstance() returned null");
}
//Get the token that was consumed by the GenericIssuedConsumeLoginModule
SecurityToken myToken = factory.getConsumerTokenFromSharedState(_sharedState,
new QName("http://www.acme.com","MyToken"));
if (myToken == null) {
throw new LoginException("no token");
}
//Get the token's element
Object obj = myToken.getXML();
if (obj == null) {
throw new LoginException("token is empty");
}
if (!(obj instanceof OMStructure)) {
throw new LoginException("XML is not OMStructure");
}
OMElement tokenElement = ((OMStructure)obj).getNode();
//you can use the org.apache.axis2.util.XMLUtils.toDOM method
//if you want to work with the a w3c.dom element instead of an
//Axiom element
//Do some processing on the contents of the token element
OMElement el = tokenElement.getFirstChildWithName(new QName("http://www.acme.com","Email"));
if (el == null) {
throw new LoginException("no email element");
}
String value = el.getText();
if (value != null && value.equals("joe.smith@acme.com")) {
return true;
} else {
throw new LoginException("email value is bad");
}
}
public boolean commit() throws LoginException {
return true;
}
public boolean logout() throws LoginException {
return false;
}
public boolean abort() throws LoginException {
return false;
}
}
- Create New JAAS login configurations.
- In the administrative console, click
- Under Authentication, click
- Create the JAAS login configuration for the custom generator.
- Click New
- Specify Alias =
test.generate.custom
- Add the MyCustomGenerator class
- Click New
- Specify Module class name =
test.tokens.MyCustomGenerator
- Click Use login module proxy
- Click OK
- Add the GenericIssuedTokenGenerateLoginModule class
- Click New
- Specify Module class name =
com.ibm.ws.wssecurity.wssapi.token.impl.GenericIssuedTokenGenerateLoginModule
- Click OK
- Click OK
- Create the JAAS login configuration for the custom consumer
- Click New
- Specify Alias =
test.consume.custom
- Add the GenericIssuedTokenConsumeLoginModule class
- Click New
- Specify Module class name =
com.ibm.ws.wssecurity.wssapi.token.impl.GenericIssuedTokenConsumeLoginModule
- Click OK
- Add the MyCustomConsumer class
- Click New
- Specify Module class name =
test.tokens.MyCustomConsumer
- Click Use login module proxy
- Click OK
- Click OK
- Click Save
- Create the custom policy set.
- Click
- Click New
- Specify Name =
CustomTokenPolicy
- Click Apply
- Under Policies, click
- Edit the custom policy set
- Click
- Remove the unwanted elements.
- Deselect Include timestamp in security header
- Deselect Message level protection
- Click Apply
- Add the custom token
- Click Request token policies
- Click , and then specify:
- Custom token name =
myToken
- Local part =
MyToken
- Namespace URI =
http://www.acme.com
- Click OK
- Click Save
- Configure the client to use the new policy set
- Click
- Click your service client
- In the check box, select the resource at the top level.
- Click Attach Policy Set
- Click ACustomTokenPolicy
- Create a custom binding for the client
- In the check box, select the resource at the top level.
- Click Assign Binding
- Click New Application Specific Binding
- Specify Bindings configuration name =
customTokenClientBinding
- Click
- If the Main Message Security Policy Bindings panel does not display,
click WS-Security
- Configure the custom bindings for the client
- Click Authentication and protection
-
Configure the custom token generator
-
Click
request:myToken
- In the JAAS login drop-down list, click
test.generate.custom.
- Click Apply
- Click Callback handler
- If the User name and Password fields are
pre-filled by your browser, clear the fields
- Under Custom properties, enter the following values:
- Name:
passThroughToken
- Value:
true
- Click OK
- Click Save
- Configure the provider to use the new policy set
- Click
- Click your service provider
- In the check box, select the resource at the top level.
- Click Attach Policy Set
- Click ACustomTokenPolicy
- Create a custom binding for the provider
- In the check box, select the resource at the top level.
- Click Assign Binding
- Click New Application Specific Binding
- Specify Bindings configuration name =
customTokenProviderBinding
- Click
- If the Main Message Security Policy Bindings panel does not display,
click WS-Security
- Configure the custom bindings for the provider
- Click Authentication and protection
-
Configure the custom token consumer
-
Click
request:myToken
- In the JAAS login drop-down list, click
test.consume.custom.
- Click Apply
- Click Callback handler
- Under Custom properties, click New, then enter the
following two custom properties:
- Name:
passThroughToken
, Value: true
- Name:
alwaysGeneric
, Value: true
- Click OK
- Click Save to save your configuration changes.
- Restart the application server to Apply the JAAS configuration
changes.
- Test your service.
Example
The following example illustrates the SOAP Security header
that is produced when you follow the preceding procedure.
<wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" soapenv:mustUnderstand="1">
<acme:MyToken xmlns:acme="http://www.acme.com" xmlns:utl="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" utl:Id="cust_3">
<acme:Email>joe.smith@acme.com</acme:Email>
</acme:MyToken>
</wsse:Security>