(V8502)Replace the authentication method of the UsernameToken consumer using a stacked JAAS login module
By default, the Web services security UsernameToken consumer, UNTConsumeLoginModule, always validates the username and password contained within the token against the WebSphere registry. We can use the SPIs that GenericSecurityTokenFactory provides to bypass this authentication method.
To replace the authentication method that UNTConsumeLoginModule uses, provide our own custom JAAS login module to do the authentication. The custom login module is stacked under UNTConsumeLoginModule in a custom JAAS configuration. The UNTConsumeLoginModule consumes and validates the token XML. The validation of the values provided for username and password is deferred to the custom stacked login module.
Because the use of UNTConsumeLoginModule carries with it the assumption that the username and password will be authenticated, more requirements are put on a stacked login module that intends to perform this function than are put on login modules that are only intended to provide dynamic token functionality.
To indicate to UNTConsumeLoginModule that it should not authenticate the username and password, set the following property on the configured callback handler:
com.ibm.wsspi.wssecurity.token.UsernameToken.authDeferred=true
Like most WS-Security login modles, UNTConsumeLoginModule always puts the consumed token in the shared state map to which all login modules in the stack have access. When authDeferred=true is specified, in the commit phase, UNTConsumeLoginModule ensures that the same UsernameToken object that had originally been put on the shared state has been put in another location in the shared state. If this UsernameToken object cannot be found, a LoginException occurs. Therefore, we cannot just set authDeferred=true on the callback handler without having an accompanying login module return the token to the shared state.
- Develop a JAAS login module to do the authentication and make it available to the application code. This new login module stacks under the com.ibm.ws.wssecurity.wssapi.token.impl.UNTConsumeLoginModule.
This login module must:
- Use the following method to get the UsernameToken that UNTConsumeLoginModule consumes.
UsernameToken unt = (UsernameToken)factory.getConsumerTokenFromSharedState(sharedState,UsernameToken.ValueType);
In this code example, factory is an instance of com.ibm.websphere.wssecurity.wssapi.token.GenericSecurityTokenFactory.
- Check the username and password in the manner that we choose.
We can call unt.getUsername() and unt.getPassword() to get the username and password.
Your login module should throw a LoginException if there is an authentication error.
- Put the UsernameToken, that was obtained in the previous substep, back on the shared state.
Use the following method to put the UsernameToken back on the shared state.
factory.putAuthenticatedTokenToSharedState(sharedState, unt);
Following is an example login module:
package test.tokens; import com.ibm.websphere.wssecurity.wssapi.token.GenericSecurityTokenFactory; 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 com.ibm.websphere.wssecurity.wssapi.token.UsernameToken; import java.util.ArrayList; public class MyUntAuthenticator 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 { //For the sake of readability, this login module does not //protect against all NPE's 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"); } UsernameToken unt = (UsernameToken)factory.getConsumerTokenFromSharedState(this._sharedState,UsernameToken.ValueType); String username = unt.getUsername(); char [] password = unt.getPassword(); //authenticate the username and password //if error, throw new LoginException //Put the authenticated token to the shared state factory.putAuthenticatedTokenToSharedState(this._sharedState, unt); return true; } //implement the rest of the methods required by the //LoginModule interface }
- Create a new JAAS login configuration.
- In the console, select Security > Global security > Authentication, select Java Authentication and Authorization Service.
- Select System logins.
- Click New, and then specify Alias = test.consume.unt.
- Click New, and then select Module class name = com.ibm.ws.wssecurity.wssapi.token.impl.UNTConsumeLoginModule
- Click New, and then specify Module class name = test.tokens.MyUntAuthenticator
- Select Use login module proxy.
- Click OK, and then click SAVE.
- Configure the UsernameToken token consumer to use the new JAAS configuration.
- Open the bindings configuration to change.
In the console, select WS-Security > Authentication and protection > Authentication tokens, select the UsernameToken inbound token to change.
- Select JAAS login = test.consume.unt.
- Set the required property on the callback handler configured for the UsernameToken consumer.
- Click Callback handler.
- Add the com.ibm.wsspi.wssecurity.token.UsernameToken.authDeferred=true custom property.
- Click OK.
- Click SAVE.
- Restart the application server to apply the JAAS configuration changes.
- Test the service.
Related tasks
Create custom security tokens for Web services security using the GenericSecurityTokenFactory SPIs