Custom Axis Handler
As we described earlier, we need something that will bridge the web service security context with the Acegi security context. This is where our custom Axis handler,
AcegiBridgeAuthenticationHandler, comes in. Its source is available in the downloadable sample code. We've kept the implementation very simple to make things easy to explain. The first thing you may notice is that it isn't actually doing any authentication. We just pull the username and password out of the
MessageContext and use them as is. A real implementation would, of course, actually try to verify that information. Something else to consider is that a real implementation should extract the WS-Security headers from the SOAP message and process them to get the authentication information, instead of just extracting them from the Axis
MessageContext object as we're doing here.
After we have the authentication information, the next step is to make that available to Acegi. This is done by creating an authentication token and setting it in the Acegi security context. Since we're doing username/password authentication, we'll create an instance of the
UsernamePasswordAuthenticationToken. Before we do that we need to specify what authorities (i.e., permissions) have been granted to this principal. This is done using the
GrantedAuthority interface and a simple implementation of that called the
GrantedAuthorityImpl. Since we're using the
RoleVoter to make access decisions, the authorities we will be granting are roles. Again, we've simplified the implementation by hardcoding it to grant the principal a role of manager, since that's what's required to invoke the
transferFunds() method on our POJO. A real implementation would probably take the username and password and look them up in a database or directory server to find out what roles are actually associated with that principal. Or, in some cases, that information might be available in the WS-Security headers in the form of SAML assertions.
In any case, once this is done, we create an instance of the
UsernamePasswordAuthenticationToken, passing in the username, password, and the granted roles. By using this form of the constructor that takes the array of
GrantedAuthority, we are actually telling Acegi that this token has already been authenticated so it should not need to authenticate it again. Next, we get the
SecurityContext by calling a static method on the
SecurityContextHolder and set the authentication token into the
SecurityContext. Now the authentication and role information are available downstream for Acegi to use to perform its security checks. Thus, we've effectively bridged the web service security context into the Acegi security context.
There are a couple of additional things to consider. First, Acegi also provides pretty robust authentication capabilities, so instead of having the Axis handler take care of authentication, you can let Acegi do that as well. To do this, create an unauthenticated authentication token by using the constructor that does not take an array of
GrantedAuthority. You will also need to make sure that the appropriate authentication provider is configured instead of using the
AnonymousAuthenticationProvider. Second, Acegi supports more than just username/password authentication. For example, if you're doing PKI-based authentication, you can use the
X509AuthenticationToken instead of the
Finally, we need to configure Axis to include this handler in the request-processing path of our service. This is done by adding the following entries to the Axis configuration files deploy.wsdd and server-config.wsdd:
<deployment xmlns="http://xml.apache.org/axis/wsdd/" xmlns:java="http://xml.apache.org/axis/ wsdd/providers/java"> <service name="FundsTransferService" provider="java:RPC" style="document" use="literal"> . . . <requestFlow> <handler type="java:com.mybank.security. AcegiBridgeAuthenticationHandler"/> </requestFlow> . . . </service> </deployment>
Separation of concerns is a key principle to developing Service Oriented Architectures. However, it needs to be applied not only at the architectural level, but at the implementation level as well. In this article, we've demonstrated how to use Axis, Spring, and Acegi to implement a secured web service that adheres to the SOA principle. As you have seen in the sample code, using this approach allowed us to minimize the cross-dependencies in the code that handled each concern of the service. The example we've shown was deliberately kept simple, but it should serve as a basis for you to develop web services with a robust security mechanism combining web services security with application-level security provided by Acegi. As mentioned earlier, a real system will most likely need to develop a handler that can process WS-Security headers and bridge them into the Acegi security context. One way to do this is to take the WSS4J, toolkit from Apache, and extend its Axis handlers to populate the Acegi security context as described in this article. You may have to do some additional work to create an Axis outbound handler that catches the Acegi security exceptions and creates more meaningful SOAP faults to return to the client.
- Complete source code, configuration files, and build file for the example used in this article. Libraries for Axis, Spring, and Acegi must be obtained from the links below in order to build and run the example.
- Apache Axis
- The Spring Framework
- The Acegi Security System For Spring
- WS-Security, the Web Service Security standards from OASIS.
- WSS4J, the Apache implementation of WS-Security.
Tieu Luu is an Associate with Booz Allen Hamilton where he works on architectures and strategies for large enterprise systems.
Return to ONJava.com.