Get user name with Apache CXF 2.4 JAX-RS and Sprin

2019-05-06 04:18发布

问题:

I get the user name in my JAX-RS resource with SecurityContextHolder and that works:

@Path("/myresource")
public class MyResoure {

     @Get
     public String getUserName() {
          return SecurityContextHolder.getContext().getAuthentication().getName();
     }
}

But I want to inject the SecurityContext into a class field (to write JUnit tests). I tried some documented ways:

With javax.ws.rs.core.SecurityContext I get a NullPointerException, because the securityContext is always null.

@Path("/myresource")
public class MyResoure {

    @Context
    private javax.ws.rs.core.SecurityContext securityContext;

    @Get
    public String getUserName() {
        return securityContext.getUserPrincipal().getName();
    }
}

With org.apache.cxf.security.SecurityContext (see Apache CXF documentation) I get a NullPointerException, because the securityContext.getUserPrincipal() is always null.

@Path("/myresource")
public class MyResoure {

    @Context
    private org.apache.cxf.security.SecurityContext securityContext;

    @Get
    public String getUserName() {
        return securityContext.getUserPrincipal().getName();
    }
}

With org.apache.cxf.jaxrs.ext.MessageContext (see Apache CXF documentation) I get a NullPointerException, because the messageContext.getSecurityContext().getUserPrincipal() is always null.

@Path("/myresource")
public class MyResoure {

    @Context
    private org.apache.cxf.jaxrs.ext.MessageContext messageContext;

    @Get
    public String getUserName() {
        return messageContext.getSecurityContext().getUserPrincipal().getName();
    }
}

With javax.servlet.http.HttpServletRequest I get a NullPointerException, because the httpServletRequest.getUserPrincipal() is always null.

@Path("/myresource")
public class MyResoure {

    @Context
    private javax.servlet.http.HttpServletRequest httpServletRequest;

    @Get
    public String getUserName() {
        return httpServletRequest.getUserPrincipal().getName();
    }

回答1:

To get the user name with

  • javax.ws.rs.core.SecurityContext,
  • org.apache.cxf.security.SecurityContext,
  • org.apache.cxf.jaxrs.ext.MessageContext or
  • javax.servlet.http.HttpServletRequest

I had to add the SecurityContextHolderAwareRequestFilter to my springSecurityFilterChain.

Spring configuration:

<bean id="springSecurityFilterChain" class="org.springframework.security.web.FilterChainProxy">
    <security:filter-chain-map path-type="ant">
        <security:filter-chain pattern="/**" filters="securityContextPersistenceFilter,authenticationProcessingFilter,securityContextHolderAwareRequestFilter,anonymousProcessingFilter,exceptionTranslationFilter,filterSecurityInterceptor" />
    </security:filter-chain-map>
</bean>

With Security Namespace Configuration or Java Configuration the filter is added by default, see Spring Security documentation:

  • servlet-api-provision Provides versions of HttpServletRequest security methods such as isUserInRole() and getPrincipal() which are implemented by adding a SecurityContextHolderAwareRequestFilter bean to the stack. Defaults to true.


回答2:

I found some solutions for my JUnit problem:

  • Setting SecurityContext in SecurityContextHolder (see https://stackoverflow.com/a/5703170)

    JUnit test code:

    public class MyResourceTest {
    
        private SecurityContext securityContextMock = mock(SecurityContext.class);
    
        @Before
        public void setUp() {
            SecurityContextHolder.setContext(securityContextMock);
        }
    
        @After
        public void tearDown() {
            SecurityContextHolder.clearContext();
        }
    }
    
  • Wrapping SecurityContextHolder (see https://stackoverflow.com/a/5702970)

    Wrapper code:

    public class MySecurityContextHolder {
        public SecurityContext getContext() {
            return SecurityContextHolder.getContext();
        }
    }
    

    Resource code:

    @Path("/myresource")
    public MyResource {
    
        private MySecurityContextHolder mySecurityContextHolder;
    
        @Get
        public String getUserName() {
             return mySecurityContextHolder.getContext().getAuthentication().getName();
        }
    }
    
  • Using SecurityContextHolderStrategy (see SEC-1188)

    Spring configuration:

    <bean id="myResource" class="MyResource">
        <property name="securityContextHolderStrategy" >
            <bean class="org.springframework.security.context.SecurityContextHolder" factory-method="getContexHolderStrategy">
            </bean>
        </property>
    </bean>
    

    Resource code:

    @Path("/myresource")
    public MyResource {
    
        private SecurityContextHolderStrategy securityContextHolderStrategy;
    
        @Get
        public String getUserName() {
             return securityContextHolderStrategy.getContext().getAuthentication().getName();
        }
    }
    
  • Using PowerMock (see https://stackoverflow.com/a/5703197/5277820)