<< White Papers | Home | The Key to Maintenance is Compartmentalisation >>

Java EE Security and Spring

Spring Security (originally ACEGI) does not seem to work out of the box with Java EE security (see here). I guess the reason is clear, namely that the people from Spring don't want you to be tied into Java EE and that makes sense because Spring something that you can use without Java EE. But instead of having to learn something new and instead of having to configure my security in a non-standard way, I wanted a way to ensure my services are only called by someone with the right authorization, exactly when they are deployed in a Java EE environment.

Since a standard web application running in a Java EE container can be configured to have security, I expected to be able to configure Spring to use the web request to check the Principal for its security roles before calling a service, but that isn't possible. So, I set to work to quickly create a little library to help me out.

The first step was to implement a Filter in the web application so that it could intercept every call and insert the web request (containing the Principal) into a ThreadLocal variable. The idea was that the ThreadLocal variable could then be used further up the call stack to query the security roles of the authenticated user. The filter implementation looks like this:

    public void doFilter(
            ServletRequest request,
            ServletResponse response,
            FilterChain chain)

            throws IOException, ServletException { 

        //set the request in the security thread local
        SecurityHelper.set((HttpServletRequest)request);

        chain.doFilter(request, response);

        //all done, so remove references to unwanted objects

        SecurityHelper.finished();
    }


The filter is configured in the Java EE web application's web.xml like this:

    <filter>
        <filter-name>maxantSpringSecurity</filter-name>
        <filter-class>
            uk.co.maxant.spring.security.SecurityFilter
        </filter-class>
    </filter>
   
    <filter-mapping>
        <filter-name>maxantSpringSecurity</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

The filter calls the SecurityHelper which uses a ThreadLocal variable to store the web request as follows. The web request can be used to gain access to the Principal, as well as ask if the Principal has a given role.

    private static ThreadLocal tls = new ThreadLocal();

    public static void set(HttpServletRequest request){
        tls.set(request);
    }

    public static boolean isInRole(String role){
        HttpServletRequest req = tls.get();
        if(req == null){
            return false;
        }else{
            return req.isUserInRole(role);
        }
    }


The Security Helper also contains an additional method which can be called to release the reference to the request. This is important because the web request contains a reference to the session and that might be large. We don't want to be holding on to such things for a long time as they may use memory unncessarily. This method can is called as soon as all security queries are finished, and is implemented as follows. For more information on thread locals and threads that are stored in pools, see here.

    public static void finished(){
        tls.remove();
    }


The final step was to configure my Spring configuration to setup an aspect to intercept all service calls. The advice which this aspect gives is whether the current user has the role required to call the method. The required role comes from an annotation added to the service methods and the current user comes from the Principal stored in the ThreadLocal from step one, above. If the user has rights, the service method is called. If not, a simple SecurityException is thrown, providing details about the missing roles. In cases where the user is not logged in, the Security Exception is also thrown. In cases where a Service method has no annotation, then no security is checked. The aspect is configured in the Spring configuration like this:  

    <!-- security aspect -->
    <bean id="security"
        class="uk.co.maxant.spring.security.SecurityAspect">
        <!-- execute before the transactional advice (hence the lower order number)
            but after profiling -->
        <property name="order" value="2" />
    </bean>

    <aop:config>
        <!-- this advice will execute around the transactional advice -->
        <aop:aspect id="securityAspect" ref="security">
            <aop:pointcut id="serviceMethod"
                expression="execution(* uk.co.maxant.gwtdemo16.server.*Service.*(..))" />
            <aop:around method="checkSecurity"
                pointcut-ref="serviceMethod" />
        </aop:aspect>
    </aop:config>

The expression in the above pointcut needs to be configured to match your package and Service naming conventions. 

The aspect itself is implemented like this:

    public Object checkSecurity(ProceedingJoinPoint call)
            throws Throwable {

        String[] requiredRoles = null;
        MethodSignature ms =
                       (MethodSignature)call.getSignature();
        Method m = ms.getMethod(); //lets check it for the
                                   // RolesAllowed annotation
        RolesAllowed roles = m.getAnnotation(RolesAllowed.class);
        if(roles != null){
            requiredRoles = roles.value();
            for(String role : requiredRoles){
                if(SecurityHelper.isInRole(role)){
                    //ok, no problem, lets continue
                    //with the call
                    requiredRoles = null; //since we can now
                                          // carry out the call
                    break;
                }
            }
        }
   
        if(requiredRoles == null){
            Object returnValue = call.proceed();
            return returnValue;
        }else{
            String rrs = "[";
            for(String s : requiredRoles){
                rrs += s + ", ";
            }
            rrs = rrs.substring(0, rrs.length() - 2) + "]";
           
            throw new SecurityException("To call [" +
                call.getSignature().toLongString() +
                "] the user must be authenticated and " +
                "authorized in these roles: " + rrs);
        }
    }
 

The annotation is defined as such:

    @Retention(value=RetentionPolicy.RUNTIME)
    @Target(value={ElementType.METHOD})
    public @interface RolesAllowed {
        String[] value();
    }


The annotation is used like this, on all service methods requiring security:

    @RolesAllowed ("registered") 

 
What I now have is a simple library which I can configure using a Filter and an Aspect. Additionally to allow the user to log in in the first place, I can use a standard Java EE security constraint in my web application. The standard steps to use this security extension of Spring would be:

  • Add a security constraint to the web application
  • Add the annotation to the service methods requiring security
  • Add the filter to the web application
  • Add the aspect to the Spring configuration
  • Add the library to the classpath

I understand that Spring does not want you to need a dependency on Java EE, but on the other hand if I have that dependency due to other requirements, I do not want to have to configure my application using a third party security concept (Spring) as opposed to a standard one (Java EE). Spring Security is not even part of Standard Spring - it is downloaded seperately. The more extensions you use, the more skills your developers need. The maxant Spring Security extension provides a very simple way to secure your service methods.

Download the library including source code here.

Copyright (c) 2009 Ant Kutschera

 

Social Bookmarks :  Add this post to Slashdot    Add this post to Digg    Add this post to Reddit    Add this post to Delicious    Add this post to Stumble it    Add this post to Google    Add this post to Technorati    Add this post to Bloglines    Add this post to Facebook    Add this post to Furl    Add this post to Windows Live    Add this post to Yahoo!


Re: Java EE Security and Spring

This is an excellent article and clearly explains how the annotation gets processed. Thanks a lot..This helps

Re: Java EE Security and Spring

Thanks alot! It helps me to complete authorization for my project

Add a comment Send a TrackBack