I'm currently trying to integrate JAX-RS with Spring Security for
authorization (authorization only; I use a custom authentication
mechanism). I found the following resources describing integration
between CXF and Spring Security:

- http://www.nabble.com/Re:-CXF%2BACEGI-%2B-Anybody-out-there--p12759358.html
(WS-Security)
- http://www.emforge.org/wiki/WebServicesImplementation (WS-Security)
- There is also a JAX-RS systest (see
systest/jaxrs/src/test/resources/jaxrs_security/WEB-INF/beans.xml in
the trunk) that integrates Spring Security with JAX-RS.

In order for (annotation driven) authorization to work, it is
necessary to use SecurityContextHolder to associate the
SecurityContext/Authentication with the current thread. In the first
two references, this is done in a custom interceptor, while the
systest uses a servlet filter (that implements HTTP basic
authentication). I see two issues with these approaches:
- The service is not necessarily invoked in the same thread as the
interceptor or servlet filter (e.g. in-only operations). If that
happens, the security context will not be set up correctly.
- The code in the first two references never resets the authentication
in the SecurityContext (by calling
SecurityContextHolder.getContext().setAuthentication(null)). I fear
that it is therefore possible that a service may accidentally get the
authentication from a previous request. This is only a problem when
using an interceptor, but using a servlet filter may not always be
possible (e.g. for WS-Security).

The approach that I use to avoid these problems is to insert a proxy
in front of the Invoker (JAXRSInvoker in my case). This proxy looks as
follows:

public class SpringSecurityInvokerProxy implements Invoker {
   private Invoker target;

   public Invoker getTarget() { return target; }
   public void setTarget(Invoker target) { this.target = target; }

   public Object invoke(Exchange exchange, Object o) {
       Authentication authentication = exchange.get(Authentication.class);
       SecurityContext securityContext = SecurityContextHolder.getContext();
       securityContext.setAuthentication(authentication);
       try {
           return target.invoke(exchange, o);
       } finally {
           securityContext.setAuthentication(null);
       }
   }
}

The Authentication object is added to the exchange by an interceptor
that implements the custom authentication mechanism. The try/finally
block here makes sure that the security context is reset right after
the invocation of the service. The corresponding configuration is:

   <jaxrs:server address="/rest">
       <jaxrs:serviceBeans>
           ...
       </jaxrs:serviceBeans>
       <jaxrs:providers>
           ...
       </jaxrs:providers>
       <jaxrs:invoker>
           <bean class="myapp.security.SpringSecurityInvokerProxy">
               <property name="target">
                   <bean class="org.apache.cxf.jaxrs.JAXRSInvoker"/>
               </property>
           </bean>
       </jaxrs:invoker>
   </jaxrs:server>

This works well for me, but I would like to know if there is a
better/easier way to achieve this.

Regards,

Andreas

Reply via email to