14 Jan 2012

Using AOP to control authorized callers of a service


Some days ago, I came across a problem regarding service management and security.
Let me first introduce our development background. We deliver libraries which are used both by us and by third parties to develop applications. They are based on Spring (2.5.6) and Hibernate (3.4). Moreover, the libraries access external Web Services and wrap them with additional functionality like data cache, database updates and so on. One of these libraries was to be accessed via a Spring service (say A) which was in charge of calling another service (let us call it B) and making additional operations. The point was, we didn't want developers to inject service B directly into their classes and call its methods, but instead use service A.

One solution could have been to publish service "B" with some kind of weird name so only service A would know and use it. But since we delivered the source code too, this solution simply was not secure enough. From the Java perspective, perhaps setting all methods in service "B" to "protected" could have been an option, provided both classes resided in the same package; but this was not the case, and refactoring one of the classes and moving it into a different package was not an option either.

After considering also other solutions like using Spring Security, I finally decided to use AOP.

AOP gives us a very elegant and straightforward answer to our requirement. And, even better, it sounds natural. Because remember, the question raised was: how can I prevent developers from accessing service B directly? And the most natural answer could be, "by avoiding access to its methods whenever the call is not coming from one of the allowed classes". In other words, check who the caller is and if it is not service A, ban it and return null or throw an exception.

So let's get to work. Spring supports AOP in several ways, including AOP Alliance's "MethodInterceptors". A class implementing this interface is designed to intercept calls to defined methods. Therefore, it implements a method, "invoke", which will receive the calling information (called class and method names, as well as arguments), process it and decide whether it permits the call to be carried out or be intercepted. 

In our case, the invoke() method will check out who the caller is, and compare its name with serviceA's class name. If they match, the proceed() method will be called, reaching its primary destination. If not, an exception will be raised and an explanatory message be printed in the log archives.

What we must develop first is a class implementing MethodInterceptor. Let's call it SecurityInterceptor, for it is going to perform security checks. This class would look similar to:

@Service
public class SecurityInterceptor implements MethodInterceptor  {
    
    private static Log LOG = LogFactory.getLog(SecurityInterceptor.class);
    
    private static final String AUTHORIZED_CALLER = "com.whattepasa.Caller"; // Could also be defined in a config file

    public Object invoke(MethodInvocation invocacion) throws Throwable {
        StackTraceElement[] trace = Thread.currentThread().getStackTrace();
        if (!checkStack(AUTHORIZED_CALLER, trace))  {
            LOG.error("Access denied");
            return null;
        }
        else {
            return invocacion.proceed();
        }
    }
    
    private boolean checkStack(String className, StackTraceElement[] trace)  {
        for (StackTraceElement traceElement : trace)  {
            if (traceElement.getClassName().indexOf(className)!=-1) {
                return true;
            }
        }
        return false;
    }
}

This code will work with Java 5 and above. Note that the class is annotated as a Spring service (@Service)

As the call does not necessarily come directly from the allowed class, since there might exist other interceptors who would interfere the original call, we must loop back through the stack, looking for the expected calling class one by one. This might slow down operations a bit but the overall impact is very low since it's just a fast loop in memory; and specially because we will invoke this interceptor only when there is a pointcut match. 

Now wait, what is a pointcut? Simply speaking, it is an expression describing when do we want the interceptor to be invoked. Pointcuts in Spring AOP follow the same syntax as in AspectJ. Not all expressions are supported by Spring, but quite a big subset of them.

In our case, the pointcut expression would be defined as follows:

<aop:pointcut expression="execution(* com.whattepasa.ServiceB.*(..))" id="security" />

Which can be read as "match all executions of any method of the ServiceB interface / class". Exactly what we want.

We must relate the pointcut (when) with the interceptor (what); that's why we added the @Service annotation to our interceptor (in AOP notation, such an interceptor is called an "advice"). Using XML-style declarations, the final configuration would look something like this:

<aop:config>
     <aop:pointcut expression="execution(* com.whattepasa.ServiceB.*(..))"              id="security" />
<aop:advisor advice-ref="securityInterceptor" pointcut-ref="security"/>
</aop:config>


And that's all it takes!

Of course, this is just an introduction. The astute reader will devise an easy way to extend the interceptor as to protect more classes, each one with a list of allowed callers. This could also be easily configured via Spring configuration files, therefore defining a very powerful way of controlling who can call which service and in which way.

For more information:

Enjoy!

No comments: