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, select Security->Global
security.
- Under Authentication, select Java Authentication
and Authorization Service.
- Select System logins.
- Create the generator with the custom module first.
- Click New, and then specify Alias
= test.generate.custom.
- Click New, and then specifyModule
class name = test.tokens.MyCustomGenerator.
- Select Use login module proxy .
- Click OK.
- Click New, and then select Module
class name = com.ibm.ws.wssecurity.wssapi.token.impl.GenericIssuedTokenGenerateLoginModule.
- Click OK.
- Click JAAS - System logins at the top of
this console page.
- Create the consumer with the custom module last.
- Click New, and then specify Alias
= test.consume.custom.
- Click New, and then select Module
class name = com.ibm.ws.wssecurity.wssapi.token.impl.GenericIssuedTokenConsumeLoginModule.
- Click New, and then specify Module
class name = test.tokens.MyCustomConsumer.
- Select Use login module proxy.
- Click OK .
- Click Save.
- Create the custom policy set.
- In the administrative console, click Services
> Policy sets > Application Policy sets.
- Click New, and then specify ACustomTokenPolicy.
- Click Apply.
- Under Policies, click Add > WS-Security.
- Edit the custom policy set.
- In the administrative console, click WS-Security
> Main Policy.
- Remove the unwanted elements:
- Unselect Include timestamp in security header.
- Unselect Message level protection.
- Add the custom token.
- Click Request token policies
- Click Add token type > Custom, 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 ACustomTokenPolicy policy
set.
- In the administrative console, click Services
> Service clients.
- Click on the desired service client.
- Select the resource at the top level.
- Click Attach Policy Set.
- Select ACustomTokenPolicy
- Create a custom binding for the client.
- Select the resource at the top level again.
- Click Assign Binding.
- Click New Application Specific Binding to
create an application-specific binding.
- Specify the bindings configuration name.
name:
customTokenClientBinding
- Click Add > WS-Security.
If the Main Message Security Policy Bindings panel does not
display, select WS-Security.
- Configure the client's custom bindings.
- Select Authentication and protection > request:myToken.
- Select JAAS login = test.generate.custom.
- Click Apply.
- Click Callback handler.
- Add the passThroughToken=true custom
property.
- Configure the provider to use the ACustomTokenPolicy policy
set.
- In the administrative console, click Services
> Service providers.
- Click on the desired service provider.
- Select the resource at the top level.
- Click Attach Policy Set.
- Select ACustomTok enPolicy.
- Create a custom binding for the provider.
- Select the resource at the top level again.
- Click Assign Binding.
- Click New Application Specific Binding to
create an application-specific binding.
- Specify the bindings configuration name.
customTokenProviderBinding
- Click Add > WS-Security.
If the Main Message Security Policy Bindings panel does not
display, select WS-Security.
- Configure the custom bindings for the provider.
- Select Authentication and protection > request:myToken.
- Select JAAS login = test.consume.custom.
- Click Apply.
- Click Callback handler.
- Add the passThroughToken=true and alwaysGeneric=true custom
properties.
- 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>