Separation of concerns (separation of concerns) is the core principle of Service-Oriented Architecture (SOA. Unfortunately, this principle is often ineffective in implementing SOA services. We usually see a huge implementation class with multiple concerns (such as security and transaction management), and use the business logic to record all the concerns mixed together. Using Spring framework and aspect oriented programming (AOP) principles, we can separate concerns for Service implementation.
Software Development Network
In this article, we will demonstrate how to use Apache axis and spring to develop Web services and use acegi security to protect them-while maintaining a good separation of focus.
Motivation and Design
In this document, we will use the fundstransferservice service, which transfers funds from one account to another. You can find the Service's WSDL and all source code, configuration files, and build files in the reference materials section of this article. We intentionally keep this service very simple to focus on more meaningful aspects of this article. In the implementation of this service, we will focus on three points:
Software Development Network
- Web Service pipeline, used to publish functions as a service
- Business logic for transferring funds
- Security, used to ensure that only authorized users can transfer funds
The real system may have to deal with other concerns, such as transaction management and logs.
We want to design such an implementation to completely separate the code that processes each focus from other code. For the Web Service pipeline, we will use axis to publish the functions of the Service. The business logic used to transfer funds from one account to another is encapsulated in a group of pojo (plain old Java object. The acegi security framework is used to provide security. We will use spring Framework and Its AOP tool to link various aspects to minimize the dependencies between all codes that constitute the Web Service implementation.
The Design 1 of this implementation is shown in. Objects in yellow are the web services we need to implement. Objects in blue are from Axis, objects in pink are from acegi, and objects in green are from spring. Fundstransferservice is the service interface defined in WSDL. To simplify the chart, we display all axis classes as components named Axis engine. Basichandler is also an axis class, but it is shown separately because it is important for design (detailed later. Fundstransferservicesoapbindingimpl is the generation class of axis, which must be implemented to provide service functions. The pojo accountmgrimpl logic will be delegated directly through spring (this will be explained in detail later ). It is good to bind accountmgrimpl with the accountmgr interface, because this allows us to insert spring to play its role (although there are other ways to use spring without interfaces ).
Software Development Network
Figure 1. fundstransferservice implementation design
Software Development Network
Now return to basichandler. The reason for the response involves how to choose to provide security. In this example, the security of web service and the security of application code, such as pojo, are handled at two different levels. According to the concept of separation of concerns, we propose a design that allows separation. Axis allows you to insert custom handlers, listen to requests, and respond to messages to provide other functions (such as security ). Therefore, we will create a custom processing procedure named acegibridgeauthenticationhandler to handle Web Service Security. Extends the axis class basichandler so that it can be inserted into the axis handler framework. Acegi will be used to provide application-level security, such as Access Control over pojo.
To make these aspects work seamlessly, you need to connect the Web service security context to the acegi security context-so the custom axis handler class name is acegibridgeauthenticationhandler. It not only handles web service security, but also connects the security context obtained from this process to the acegi environment, so that acegi can decide whether to authorize access to pojo. The method is to extract the security statement from the Web service request message for verification, and then create an authentication token, because we have selected username/password authentication for this example, so it is usernamepasswordauthenticationtoken. Then, set the authentication token to acegi securitycontext, so that acegi can use the requester's certificate and permissions when controlling the access business logic pojo later.
The following explains how to use spring to link various aspects. The magic weapon is the small green object named AOP proxy in figure 1, which is generated by spring. This object implements the accountmgr interface and acts as a proxy for the business logic pojo accountmgrimpl. This allows spring to insert the methodsecurityinterceptor of acegi. When someone tries to call a method on pojo, the access control check is executed. When fundstransferservicesoapbindingimpl delegates a web service request to pojo, it actually delegates the request to the instance of the AOP proxy, rather than directly delegates the request to accountmgrimpl. Because fundstransferservicesoapbindingimpl extends servletendpointsupport, it can access the context of the spring application to obtain reference accountmgrimpl for the AOP agent with the correct interfaces, listeners, and Target classes configured.
Software Development Network
The sequence diagram in Figure 2 demonstrates the process that will occur when the client calls fundstransferservice. The request message is received by Axis engine and then acegibridgeauthenticationhandler is called. Acegibridgeauthenticationhandler will verify the authentication information and then create usernamepasswordauthenticationtoken. Then, set the token to the securitycontext of acegi for later use. After acegibridgeauthenticationhandler returns a successful result, axis engine calls the transferfunds () method on fundstransferservicesoapbindingimpl. Fundstransferservicesoapbindingimpl delegates it to the AOP agent. during initialization, it can be obtained from the context of the spring web application earlier. The AOP proxy calls acegi methodsecurityinterceptor to perform its security check. Methodsecurityinterceptor obtains the authentication token from securitycontext and checks whether it has been authenticated. Next, we will use the information in the authentication token to determine whether to authorize access from the client, and call the transferfunds () method on accountmgrimpl. If the client is allowed for access, methodsecurityinterceptor will allow you to call this method and then enter accountmgrimpl. Finally, accountmgrimpl processes the request, returns the result, and finally sends it back to the client program.
Figure 2. Sequence diagram of the service implementation process
Business Logic implementation and Configuration
Software Development Network
Our discussion begins with interpreting the implementation and configuration of business logic classes, because they are the simplest. For the source code of the accountmgr interface and accountmgrimpl class, see references. From the source code, we can find that the implementation does not actually do anything, so we can keep it simple, because this article is not about how to write the transfer code.
The following is part of the spring configuration file code (you can obtain the entire configuration file in the reference section). These codes demonstrate how to configure spring beans for business logic so that you can use spring's AOP tool. The first bean entry is to create a bean for the accountmgrimpl class. The second bean entry displays all the AOP proxy magic discussed above. Use the ID accountmgr obtained from proxyfactorybean to create the bean. When the fundstransferservicesoapbindingimpl class requests a bean with this ID from spring, proxyfactorybean returns an instance of the AOP proxy object. Configure it to implement the accountmgr interface, so that client programs will think that they are only using business logic objects. Use the second attribute named interceptornames to create a bean named securityinterceptor (which will be released later). You can listen on method calls for security checks. This allows us to insert an acegi security mechanism without any dependencies into the business logic code. Finally, set the target to accountmgrtarget bean, so that the method call will finally be sent to the actual business logic class accountmgrimpl.
<Beans> <Bean id = "accountmgrtarget" Class = "com. mybank. bizlogic. accountmgrimpl"/> ... <Bean id = "accountmgr" Class = "org. springframework. AOP. framework. Proxyfactorybean "> <Property name = "proxyinterfaces"> <List> <Value> Com. mybank. bizlogic. accountmgr </Value> </List> </Property> <Property name = "interceptornames"> <List> <Value> Securityinterceptor </Value> </List> </Property> <Property name = "target"> <Ref bean = "accountmgrtarget"/> </Property> </Bean> ... </Beans> |
Web service implementation and Configuration
The fundstransferservicesoapbindingimpl class is implemented by web service. For the source code, see references. The framework of this class is generated by axis. We only fill in the method to provide implementation. Note that this class extends servletendpointsupport. This is a convenient class provided by spring, which can be used for JAX-RPC web service implementation to obtain references to spring application context. By extending this class, the fundstransferservicesoapbindingimpl class can access the spring context to obtain the reference of the previously described accountmgr bean. Since the fundstransferservicesoapbindingimpl class is managed by axis, we cannot use spring's dependency injection tool to automatically obtain the reference of this bean. Therefore, it must be explicitly executed in the oninit () method. Unfortunately, this will add some dependencies in this class to spring-specific classes. Well, this is a small price to pay when spring and acegi are used to benefit. Note that in the actual method transferfunds (), the code is only delegated to accountmgr bean.
In the Axis configuration file (deploy. WSDD and server-config.wsdd), make sure that the implementation class of the service is set to this class fundstransferservicesoapbindingimpl, not other framework classes (fundstransferservicesoapbindingskeleton ). To make spring work correctly in the same web application as axis, we need to add the following entries to the Web. xml file. The context-Param entry specifies the location where the spring configuration file is placed. Listener entries are used to load spring configurations and context at startup.
<Web-app>
<Context-param>
<Param-Name>
Contextconfiglocation
</Param-Name>
<Param-value>
/WEB-INF/spring-config.xml
</Param-value>
</Context-param>
<Listener>
<Listener-class>
Org. springframework. Web. context.
Contextloaderlistener
</Listener-class>
</Listener>
...
</Web-app>
Acegi Security Configuration
Now we will discuss how to configure acegi security in the spring configuration file. As described above, we have configured the business logic bean. Therefore, securityinterceptor bean listens for method calls to perform security checks. The following describes how to configure the bean. The following is the sample spring configuration file code of securityinterceptor bean. Securityinterceptor bean is provided by the acegi class named methodsecurityinterceptor. As shown in its name, this class is used to enhance the security of method calls by listening for calls and checking whether the caller has been authenticated and authorized.
<Beans>
...
<Bean id = "securityinterceptor"
Class = "org. acegisecurity. Intercept. method.
Aopalliance. methodsecurityinterceptor "> http://www.mscto.com
<Property name = "authenticationmanager">
<Bean class = "org. acegisecurity.
Providers. providermanager ">
<Property name = "providers">
<List>
<Bean class = "org. acegisecurity. providers. Anonymous.
Anonymousauthenticationprovider ">
<Property name = "key" value = "changethis"/>
</Bean>
</List>
</Property>
</Bean>
</Property>
<Property name = "accessdecisionmanager">
<Bean class = "org. acegisecurity. Vote. unanimousbased">
<Property name = "decisionvoters">
<List>
<Bean class = "org. acegisecurity. Vote. rolevoter"/>
</List>
</Property>
</Bean>
</Property>
<Property name = "objectdefinitionsource">
<Value>
Com. mybank. bizlogic. accountmgr. transferfunds = role_manager
</Value>
</Property>
</Bean>
...
</Beans>
We need to configure the securityinterceptor bean with the authenticationmanager attribute to specify which type of authentication is used. Because our design relies on the axis handler for authentication, and authentication is not required here, we only use anonymousauthenticationprovider for configuration. In addition, the way we create an authentication token in the axis handler will make acegi know that it has been authenticated, so it will not try to authenticate again. We will give a more detailed explanation of the Axis handler later.
Next, we need to configure the bean with the accessdecisionmanager attribute to specify how to determine whether to authorize a user to access to call the method. Acegi has three specific implementations: affirmativebased, consensusbased, and unanimousbased. Acegi implements an access policy based on the vote that the voter authorizes a user to perform a specific operation. Calculate the number of votes to determine whether access should be authorized. We chose the unanimousbased access decision manager. In order for the client to perform operations, all voting requests are required for access authorization. You should read the acegi document to learn more about the access decision. Next, we must configure accessdecisionmanager with a voter list. Here we only use a voter named rolevoter. This is a class from acegi, which votes on whether to authorize access based on role access control. Users should also refer to the acegi document for more in-depth instructions on rolevoter's working methods.
The last attribute to be configured is objectdefinitionsource. This is how to specify the authentication required by different methods to access protected objects. Here, we only want to protect the transferfunds () method and only allow access to the manager. By listing all qualified class names, method names, and the roles required for access:
Com. mybank. bizlogic. accountmgr. transferfunds = role_manager
Custom axis Handler
As described earlier, we need something to connect the Web service security context and acegi security context. The custom axis handler acegibridgeauthenticationhandler is used here. We maintain a very simple implementation, which is easy to explain. The first thing that may attract attention is that it does not actually perform any authentication. We only get the username and password from messagecontext, and then use them as usual. Of course, the real implementation will try to verify the information. The thing to consider is that the real implementation should extract the WS-Security header from the SOAP message and then process it to obtain authentication information, rather than simply extracting them from the axis messagecontext object as we do.
After obtaining the authentication information, you can use it for acegi. You can create an authentication token and set it in the acegi security context. Because username and password authentication is performed, an instance of usernamepasswordauthenticationtoken will be created. Before creating a subject, you must specify the permissions granted to the subject. You can use the grantedauthority interface and simple implementation called grantedauthorityimpl. Since rolevoter is used for access decision-making, we will authorize the Role of the Organization. Here, we simplified the implementation again, hardcoded it, and authorized the main manager role, because this role is required to call the transferfunds () method on pojo. The real implementation may obtain the user name and password, and then search in the database or directory server to find the role actually related to the subject. Alternatively, you can obtain this information in SAML declaration form in the WS-Security header in some cases.
In all circumstances, once completed, a usernamepasswordauthenticationtoken instance will be created to pass the user name, password, and authorized role. By using this Constructor (using the grantedauthority array), The accesskey actually notifies acegi that the token has been authenticated, so you do not have to authenticate it again. Next, obtain the securitycontext by calling the static method on securitycontextholder, and set the authentication token to securitycontext. Authentication and role information are currently available downstream for acegi and used for security checks. Therefore, we have effectively connected the Web service security context to the acegi security context.
There are several other things to consider. First, acegi also provides very reliable authentication capabilities. Therefore, it is not necessary for the Axis handler to focus on authentication, but for acegi to do these tasks. To do this, use the constructor that does not use the grantedauthority array to create an unauthenticated authentication token. You also need to ensure that the appropriate authentication provider is configured, instead of using anonymousauthenticationprovider. Second, acegi not only supports user name/password authentication, but also supports other authentication. For example, if you are performing PKI-based authentication, you can use x509authenticationtoken instead of usernamepasswordauthenticationtoken.
Finally, you need to configure axis to include the handler in the request processing path of the service. By adding the following entries to the axis configuration file 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>
In this article, we demonstrate how to use axis, spring, and acegi to implement a protected web service that complies with the SOA principles. As shown in the sample code, this method is used to minimize the cross dependency in the code that processes each service focus. The example we provide is deliberately simple, but should be used as the basis for developing a reliable security mechanism (combining Web Service Security and application-level security provided by acegi). As mentioned above, real systems may need to develop handlers to process WS-Security headers and connect them to the acegi security context. One way is to use the Apache toolkit wss4j and then extend its axis handler to fill the acegi security context, as described in this article. You may need to do some other work to create an axis outbound handler that captures acegi security exceptions and create a more meaningful soap error that is returned to the client.