3

Is it possible to use Context annotation and RolesAllowed annotation in a JAX-RS resource with Apache CXF 2.4.6 and Spring Security 3.2.8?

My CXF configuration:

<jaxrs:server address="/example">
    <jaxrs:serviceBeans>
        <ref bean="myResourceImpl"/>
    </jaxrs:serviceBeans>
</jaxrs:server>

My Java source code:

@Path("/myresource")
public interface MyResource {

    @GET
    @Produces(MediaType.TEXT_XML)
    String get();
}

@Named
public class MyResourceImpl implements MyResource {

    @Context
    private SecurityContext securityContext;

    @Override
    @RolesAllowed("ROLE_user")
    public String get() {
        return securityContext.getUserPrincipal().getName();
    }
}

After starting the server, I get the following exception:

Caused by: java.lang.IllegalArgumentException: Can not set javax.ws.rs.core.SecurityContext field MyResourceImpl.securityContext to com.sun.proxy.$Proxy473
    at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:164)
    at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:168)
    at sun.reflect.UnsafeFieldAccessorImpl.ensureObj(UnsafeFieldAccessorImpl.java:55)
    at sun.reflect.UnsafeObjectFieldAccessorImpl.set(UnsafeObjectFieldAccessorImpl.java:75)
    at java.lang.reflect.Field.set(Field.java:741)
    at org.apache.cxf.jaxrs.utils.InjectionUtils$1.run(InjectionUtils.java:164)
    at java.security.AccessController.doPrivileged(Native Method)
    at org.apache.cxf.jaxrs.utils.InjectionUtils.injectFieldValue(InjectionUtils.java:160)
    at org.apache.cxf.jaxrs.utils.InjectionUtils.injectContextProxiesAndApplication(InjectionUtils.java:912)
    at org.apache.cxf.jaxrs.JAXRSServerFactoryBean.injectContexts(JAXRSServerFactoryBean.java:354)
    at org.apache.cxf.jaxrs.JAXRSServerFactoryBean.updateClassResourceProviders(JAXRSServerFactoryBean.java:380)
    at org.apache.cxf.jaxrs.JAXRSServerFactoryBean.create(JAXRSServerFactoryBean.java:145)
    ... 59 more

If I remove one of the annotations, it works fine.

The problem seems to be that Spring creates a proxy and Apache CXF cannot inject that proxy with the SecurityContext.

I have to use Spring Security and cannot use container-based security.

Pang
  • 9,564
  • 146
  • 81
  • 122
dur
  • 15,689
  • 25
  • 79
  • 125
  • Possible duplicate of [Can I use @RolesAllowed on RESTful Resources implemented on Apache CXF?](http://stackoverflow.com/questions/16433315/can-i-use-rolesallowed-on-restful-resources-implemented-on-apache-cxf) – cassiomolin Oct 20 '15 at 15:43

2 Answers2

2

I found four work-arounds:

  1. Extended Interface

     @Path("/myresource")
     public interface MyResource {
    
         @Context 
         public void setSecurityContext(Security securityContext); 
    
         @GET
         @Produces(MediaType.TEXT_XML)
         String get();
     }
    
     @Named
     public class MyResourceImpl implements MyResource {
    
         private SecurityContext securityContext;
    
         @Override
         public void setSecurityContext(Security securityContext) { 
             this.securityContext = securityContext
         }
    
         @Override
         @RolesAllowed("ROLE_user")
         public String get() {
             return securityContext.getUserPrincipal().getName();
         }
     }
    

    But this solution is not perfect, because my client should not see implementation details.

  2. Dedicated interface

    If I add a second interface with a public setter for SecurityContext, Apache CXF could inject the JDK proxy with SecurityContext.

     public interface ContextAware { 
    
         @Context 
         public void setSecurityContext(Security securityContext); 
     } 
    
     @Path("/myresource")
     public interface MyResource {
    
         @GET
         @Produces(MediaType.TEXT_XML)
         String get();
     }
    
     @Named
     public class MyResourceImpl implements MyResource, ContextAware  {
    
         private SecurityContext securityContext;
    
         @Override
         public void setSecurityContext(Security securityContext) { 
             this.securityContext = securityContext
         }
    
         @Override
         @RolesAllowed("ROLE_user")
         public String get() {
             return securityContext.getUserPrincipal().getName();
         }
     }
    
  3. CGLIB proxy without interface

    If I remove the interface Spring uses a CGLIB proxy.

     @Named
     @Path("/myresource")
     public class MyResourceImpl {
    
         @Context
         private SecurityContext securityContext;
    
         @RolesAllowed("ROLE_superadmin")
         @GET
         @Produces(MediaType.TEXT_XML)
         public String get() {
             return securityContext.getUserPrincipal().getName();
         }
     }
    

    But this solution is not good, because my client should not see implementation details. And my client should not need implementation dependencies.

  4. CGLIB proxy with interface

     @Path("/myresource")
     public interface MyResource {
    
         @GET
         @Produces(MediaType.TEXT_XML)
         String get();
     }
    
     @Named
     public class MyResourceImpl implements MyResource {
    
         @Context
         private SecurityContext securityContext;
    
         @Override
         @RolesAllowed("ROLE_user")
         public String get() {
             return securityContext.getUserPrincipal().getName();
         }
     }
    
Pang
  • 9,564
  • 146
  • 81
  • 122
dur
  • 15,689
  • 25
  • 79
  • 125
0

I took a slight variation on the solution from @dur. Instead of having the @Context as a field, I passed it as a parameter to my method that needed it (I was using SecurityContext):

@Path("/myresource")
public interface MyResource {
    @GET
    @Produces(MediaType.TEXT_XML)
    String get(@Context SecurityContext securityContext);
}

@Named
public class MyResourceImpl implements MyResource {
    @Override
    @RolesAllowed("ROLE_user")
    public String get(SecurityContext securityContext) {
        return securityContext.getUserPrincipal().getName();
    }
}
Pang
  • 9,564
  • 146
  • 81
  • 122
pioto
  • 2,472
  • 23
  • 37