Apache Rampart is the Axis2 module that implements the WS-Security functionality based on Apache WSS4J. WS-Security provides multiple ways in which one can authenticate a user when they need to access a service. One easy mechanisms is to use a UsernameToken. Following figure shows how the user credentials are placed in the SOAP message when a UsernameToken is used. There are two forms of UsernameToken depending on the way the password is represented. Using a Digest of the Actual PasswordIn the case where the communicating parties - the requester and the service - uses an insecure transport channel they should take steps to protect the passwords being exposed to others. Here the requester creates a digest of the actual password concatenated with a set of random bytes (nonce) and another value that is dependent on the creation time (created). This digest is computed as follows : digest = Base64_encode(SHA-1(nonce+created+password)) This is the base 64 encoded, SHA-1 digest of the concatenation of the nonce, created and password values. The requester will send the username, nonce, created, and the digest values in within the UsernameToken to the service. To authenticate the request the service will compute the digest value using the password bound to the received usename and will compare the received digest value and the computed digest value. An example UsernameToken that uses a digest of the password is shown in following figure: Using a Plain Text PasswordThis is where the username token carries the actual password in it. One must use a secure transport such as HTTPS when the plain text password configuration is used. Now let's get our hands dirty and see how we can use Rampart to carryout user authentication using the above two modes. Sample Source CodeThe samples can be downloaded here. When you extract the zip file you can find the ?rampart-ut-samples? directory. This directory contains all the related components required to compile and run the samples. You will need JDK 1.4 and Apache Ant version 1.6.2 or later installed.
There are three samples here and each of the sample directories contains the services.xml file for the service of that sample and the sample client's axis2.xml file. It's important to look into these two files since this is where we specify all the configuration. You can use the Apache Ant build script to start the service of each sample and to run the client of that sample as shown below: To run start sample-1 service, open up a new shell and run $ ant service.01 from the rampart-ut-samples directory where you have the build.xml file. Now to run the client to send a request to that service, use another shell instance and use the following command from that same directory. $ ant client.01 When one runs the clients and the services as shown above, it will create a temporary "build" directory and will create the client and the service repositories there. For example, client repository of sample 01 is created in the following location. rampart-ut-samples/build/client_repositories/sample01 IMPORTANT: To view the SOAP messages being exchanged between the service and the client you will have to use the TCPMon tool. Simply start TCPMon listener instance to listen on port 9080 and point to the port 8080 of localhost. Now let's see how we can use Rampart. Engaging RampartTo provide the security functionality one will have to first engage the Rampart Axis2 module. The first sample (Sample-01) engages Rampart at the service and at the client. The services.xml file of sample01 is available in: rampart-ut-samples/sample01/services.xml In rampart-ut-samples/sample01/services.xml file you can will be able to see the entry: <module ref="rampart" /> This entry engages Rampart on the service. Also the same entry is available in the rampart-ut-samples/sample01/client.axis2.xml file which engages Rampart on the client. Now first start the service and then run the client while having the tcpmon setup as explained in the "Sample source code" section. When you run the client you will be able to see the following in TCPMon. As you can see the soap:Header element is empty and there are no "Security" header elements. Here we learn a very important point on Apache Rampart. "Simply engaging Rampart will not apply any security on requests and responses. One has to configure Rampart to be able to apply security mechanisms on SOAP messages". Now let's look at how we can configure Apache Rampart to add a UsernameToken to a request message and how we can authenticate it at the service. Adding a UsernameToken and Authenticating it with RampartThe client's Rampart configuration will have to be set up to add a UsernameToken and the service's Rampart configuration has to be set up to authenticate it. Client ConfigurationClient will have to provide the user name and password to be added to the Username token. User can provide these information in the outflow configuration of Rampart as shown below: <parameter name="OutflowSecurity"> <action> <items>UsernameToken</items> <user>bob</user> <passwordCallbackClass>org.apache.rampart. samples.sample02.PWCBHandler</passwordCallbackClass> </action> </parameter> This configuration parameter should be included in the client's axis2.xml file and in sample-02 the file is "rampart-ut-samples/sample02/client.axis2.xml". Let's understand different elements of the above
The Password Callback HandlerThis is a class that user has to develop, implementing the java.auth.CallbackHandler interface. The implementation for sample-02 is shown below: public class PWCBHandler implements CallbackHandler { public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException { for (int i = 0; i < callbacks.length; i++) { WSPasswordCallback pwcb = (WSPasswordCallback)callbacks[i]; String id = pwcb.getIdentifer(); if("bob".equals(id)) { pwcb.setPassword("bobPW"); } } } } The callback handler implementation provided by the user will be called with a org.apache.ws.security.WSPasswordCallback instance, in which one should set the password corresponding the user name available in the identifier. Now the client side configuration is ready. Let's move into configuring the service. Service ConfigurationWe will be configuring Rampart to expect and authenticate a UsernameToken in the request directed to the service. This requires the use of an Axis2 parameter called "InflowSecurity" as shown below: <parameter name="InflowSecurity"> <action> <items>UsernameToken</items> <passwordCallbackClass>org.apache.rampart .samples.sample02.PWCBHandler</passwordCallbackClass> </action> </parameter> This parameter should be included in the services.xml file of the service and "rampart-ut-samples/sample02/services.xml" is the services.xml file used in sample-02 service. In this configuration parameter we use the "items" element to indicate that Rampart should expect a UsernameToken element and "passwordCallbackClass" element is used to obtain the CallBackHandler implementation to access the password of the user to carryout authentication. This allows us to check our internal user store of the service to find the given user and to set the user's password in the org.apache.ws.security.WSPasswordCallback instance that is passed into the handle() method of the CallbackHandler implementation. One can package this class with the service archive since Rampart uses the service classloader to pickup this class. Now we are all set to go. Let's run sample-02 and see Rampart in action. You will be able to see the following in TCPMon : At this point we can see that there are two more elements other than "Username" and "Password" element within the "UsernameToken" element. Those are the "Nonce" and the "Created" element. Also note that the password is not the password we set using the callback handler. This is because by default Rampart/WSS4J uses password digest mechanism on UsernameToken. Also it's important to note that Rampart will only authenticate the UsernameToken in the case where password digest is used. Now let's see how we can send a plain text password to the service since this is one of the most used scenarios in practice. UsernameToken with a Plain Text Password and Authenticating ItWe can change the type of password used in the UsernameToken with a very simple change in the client's configuration. But we will have to carryout authentication at the service since Rampart will not authenticate the user. Let me explain why Rampart won't do it. First of all, in practical user credential stores we do not store the users' passwords. Therefore, since the user store doesn't hold on to the password Rampart will not be able to obtain it to carryout authentication. Also usually we store a digest of the actual password instead of the password and match the digest value of password supplied by the user against the stored digest. And this storage mechanism and format can vary depending on the implementation. Rampart simply delegates the authentication task to the service developer when it receives a UsernameToken with a plain text password. Client ConfigurationThere is only one addition to the client's Rampart configuration parameter to set the password type to plain text. <parameter name="OutflowSecurity"> <action> <items>UsernameToken</items> <user>bob</user> <passwordCallbackClass>org.apache.rampart .samples.sample03.PWCBHandler</passwordCallbackClass> <passwordType>PasswordText</passwordType> </action> </parameter> Note that we use a "passwordType" element where we set the value to PasswordText. This is used in sample-03-"rampart-ut-samples/sample03/client.axis2.xml" Service ConfigurationThere's no changes to the service configuration from that of sample-02. But we will have to change the CallbackHandler implementation used at the service to carryout authentication of the username, password pair. "sample03/src/org/apache/rampart/samples/sample03/PWCBHandler.java" demonstrates how this is done. Note that the following code segment extracted from the above PWCBHandler.java : WSPasswordCallback pwcb = (WSPasswordCallback)callbacks[i]; if (pwcb.getUsage() == WSPasswordCallback.USERNAME_TOKEN_UNKNOWN) { if(pwcb.getIdentifer().equals("bob") && pwcb. getPassword().equals("bobPW")){ return; } else { throw new UnsupportedCallbackException(callbacks[i], "check failed"); } } In the case where Rampart processes a UsernameToken with a plain text password, it will call the CallbackHandler implementation with a WSPasswordCallback object holding both username and password that came in, with the usage of the WSPasswordCallback object set to WSPasswordCallback.USERNAME_TOKEN_UNKNOWN. We can simply carryout our own check as shown above and throw an exception in the case of an authentication failure. Hosting sample-03 service and running its client, we can see the UsernameToken is uses a plain text password. Is This Secure?No!!! Sending a UsernameToken as in sample-03 is not secure at all. Anyone who monitors the communication can easily get hold of the a user's username and passwod. Therefore, it's best practice that one uses a secure transport when using UsernameToken authentication. For example, HTTPS. Or if a UsernameToken is to be used in any other situation over a transport that is not secure, one must ensure the UsernameToken element is encrypted. Enjoy Apache Rampart ! |
UsernameToken Authentication with Rampart
最新推荐文章于 2021-12-29 23:14:58 发布