将@Context和@Roles组合在JAX-RS资源中吗?

问题描述 投票:2回答:2

是否可以在具有Apache CXF 2.4.6和Spring Security 3.2.8的JAX-RS资源中使用Context注释和RolesAllowed注释?

我的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创建了proxy,而Apache CXF无法使用SecurityContext注入该代理。

我必须使用Spring Security,不能使用container-based security

java spring spring-security jax-rs cxf
2个回答
2
投票

我发现了四个解决方法:

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

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

  2. Dedicated interface

    [如果我添加带有SecurityContext的公共设置程序的第二个接口,Apache CXF可以将JDK proxySecurityContext注入。

    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

    如果删除接口,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();
        }
    }
    

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

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

0
投票

我对@dur的解决方案进行了一些改动。我没有将@Context作为字段,而是将其作为参数传递给需要它的方法(我正在使用SearchContext):

@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();
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.