在JAX-RS资源中组合@Context和@RolesAllowed



是否可以在Apache CXF 2.4.6和Spring Security 3.2.8的JAX-RS资源中使用上下文注释和角色允许注释?

CXF配置:

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

Java源代码:

@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();
    }
}

启动服务器后,我得到以下异常:

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

如果我删除其中一个注释,它会正常工作。

问题似乎是Spring创建了一个代理,而Apache CXF不能用SecurityContext注入该代理。

我必须使用Spring Security,不能使用基于容器的安全性

我找到了四个变通方法:

  1. 扩展接口
     @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();
         }
     }
    

    但是这个解决方案并不完美,因为我的客户不应该看到实现细节。

  2. 专用接口

    如果我为SecurityContext添加第二个带有公共设置器的接口,Apache CXF就可以用SecurityContext注入JDK代理。

     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代理

    如果我删除接口,Spring将使用CGLIB代理。

     @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();
         }
     }
    

    但是这个解决方案不是很好,因为我的客户端不应该看到实现细节。我的客户端不应该需要实现依赖。

  4. CGLIB代理与接口

     @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();
         }
     }
    

我对@dur的解决方案做了一些改动。我没有将@Context作为一个字段,而是将它作为一个参数传递给我需要它的方法(我使用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();
    }
}

最新更新