Separation of Concerns in Web Service Implementationsby Tieu Luu
Separation of concerns is a core principle of Service-Oriented Architectures. Unfortunately, this principle is often lost when it comes to the implementations of SOA services. All too often we see a big implementation class with multiple concerns such as security, transaction management, and logging all mixed in with the business logic. Using the Spring Framework and principles of Aspect Oriented Programming (AOP), we can drive the separation of concerns down into the implementation of services.
Motivation and Design
The example we will use in this article is a service called
FundsTransferService, which a bank might use to transfer funds from one account into another. The WSDL for this service is available along with all the source code, configuration files, and build file in the Resources section of this article. We've deliberately kept this service very simple so that we can focus on the more relevant aspects of the article. In the implementation of this service, we will be dealing with three concerns:
- Web service plumbing to expose the functionality as a service
- Business logic for the transferring of funds
- Security to ensure only authorized entities can perform the funds transfer
A real system would most likely have to deal with additional concerns such as transaction management, logging, etc.
We want to design an implementation such that the code specifications that handle each concern are cleanly separated from one another. For the web service plumbing, we will use Axis to expose the functionality as a service. The business logic for transferring funds from one account into another is encapsulated in a set of POJOs (Plain Old Java Objects). Security will be provided by the Acegi Security framework. We will use the Spring Framework and its AOP facilities to tie everything together to minimize the dependencies across all of the code that makes up the implementation for this web service.
The design for this implementation is shown in Figure 1. The objects in yellow are the ones that we need to implement. The ones in blue are from Axis; the ones in pink are from Acegi; and the ones in green are from Spring.
FundsTransferService is the interface for our service as defined in the WSDL. To simplify the diagram, we've shown all the Axis classes as a component called Axis Engine. The
BasicHandler is also an Axis class, but we've shown that separately because it is significant to our design (more on that later).
FundsTransferServiceSoapBindingImpl is a generated class from Axis that we need to implement to provide the service functionality. It will delegate to the business logic POJO
AccountMgrImpl indirectly through Spring (this will be explained in more detail later as well).
AccountMgrImpl is wrapped with an
AccountMgr interface because it's good practice, and because it also allows us to insert Spring to do its magic (although there's another way to use Spring without the interface).
Now back to the
BasicHandler. The reason this is needed is related to how we've chosen to provide security. In this example, we're dealing with security at two different levels: security for the web service, and security for the application code, i.e., the POJOs. In the spirit of separation of concerns, we've come up with a design that allows us to split this up. Axis allows us to plug in custom handlers that will intercept request and response messages to provide additional functionality such as security. Thus, we will create a custom handler called
AcegiBridgeAuthenticationHandler that will be responsible for dealing with web service security. It extends the Axis class
BasicHandler so that we can plug it into the Axis handler framework. Acegi will be used to provide the application-level security, i.e., providing access control on the POJOs.
To get both of these to work together seamlessly, we need to bridge the web service security context into the Acegi security context--hence the name
AcegiBridgeAuthenticationHandler for our custom Axis handler class. Not only will it handle the web service security processing, it will also be responsible for bridging the security context obtained from that processing into the Acegi environment, so that Acegi can then decide whether or not to grant access to the POJOs. It does this by extracting the security claims out of the web service request message, verifying them, and then creating an authentication token, specifically a
UsernamePasswordAuthenticationToken because we've chosen username/password authentication for this example. It then sets this authentication token into the Acegi
SecurityContext so that the requester's credentials and permissions are available to Acegi later when it is controlling access on the business logic POJOs.
Now to explain how we use Spring to tie everything together. The magic lies in the little green object in Figure 1 called AOP proxy, which is generated by Spring. This object implements our
AccountMgr interface and acts as a proxy to our business logic POJO
AccountMgrImpl. This allows Spring to plug in Acegi's
MethodSecurityInterceptor to perform access-control checks when someone tries to invoke methods on our POJO. When
FundsTransferServiceSoapBindingImpl delegates web service requests to our POJO, it is actually delegating to an instance of the AOP proxy instead of directly to
ServletEndpointSupport, it can access the Spring application context to get a reference to the AOP proxy, which we've configured with the proper interface, interceptors, and target class,
The sequence diagram in Figure 2 shows the flow of processing that occurs when a client invokes the
FundsTransferService. The request message is received by the Axis Engine, which then invokes the
AcegiBridgeAuthenticationHandler verifies the authentication information and then creates a
UsernamePasswordAuthenticationToken. Next, it sets this token into the
SecurityContext for Acegi to use later. After the
AcegiBridgeAuthenticationHandler successfully returns, the Axis Engine then calls the
transferFunds() method on
FundsTransferServiceSoapBindingImpl delegates this to the AOP proxy, which it obtained earlier during initialization from the Spring web application context. The
AOP proxy calls the Acegi
MethodSecurityInterceptor so that it can do its security checks. The
MethodSecurityInterceptor gets the authentication token from the
SecurityContext and checks whether it has already been authenticated. Next, it uses the information in the authentication token to see if the client should be granted access to invoke the
transferFunds() method on
AccountMgrImpl. If the client is allowed access, then the
MethodSecurityInterceptor allows invocation of that method to proceed to the
AccountMgrImpl finally processes that request and returns the results, which are ultimately propagated back to the client program.