Search code examples
springrestjax-rscxfws-security

How to inject ResourceInfo in a ContainerRequestFilter in CXF


I am trying to build a token based authentication and authorization system using Apache CXF and Spring. I am completely following this fantastic post for doing this. But I am stuck in a problem with my AuthorizationFilter at the very beginnig. I have seen many posts, apache JIRA, github comments about it, but could not yet found the workaround about this seemingly CXF issue.

@PreMatching
@Priority(Priorities.AUTHORIZATION)
public class AuthorizationFilter implements ContainerRequestFilter {
private ResourceInfo resourceInfo;

@Context
public void setResourceInfo(ResourceInfo resourceInfo) {
    this.resourceInfo = resourceInfo;
}

@Override
public void filter(final ContainerRequestContext requestContext) throws IOException {

    Method method = resourceInfo.getResourceMethod();

In the above code, the injected resourceInfo is a proxy object, and it has none of the associated properties present. So, anything from that resourceInfo object is returning null, more specifically resourceInfo.getResourceMethod() is null, leading to NPE. This is the JIRA post pertaining to this very issue, saying that:

In some circumstances (like using @ApplicationScoped annotation for example) the CDI runtime will create a proxy class for a particular bean. As the result, the CXF side is going to bind the particular provider metadata to this proxy instance. It looks logical and unambiguous.

However, the interesting things are happening when CXF will try to inject contextual proxies (@Context annotations) into the provider instance. The injections are successful but the target object for them will be the proxy instance (not the real instance behind it). Consequently, at runtime, when the proxy delegates the call to a backing instance, all contextual proxies are null in there (simply put, not set).

Referring to the recent discussions with Sergey Beryozkin, the best solution would be to delegate the @Context annotation to CDI framework (as such, relieving the CXF from doing the injection work). This proposal may need a support from the JAX-RS specification side.

Simpler (interim?) possible solution would be to complement the CDI injection with @Context injection (delegating this work to the CXF as it works right now for non-proxy beans and non-CDI deployments). This could be done by observing ProcessInjectionTarget events and supplying our own InjectionTarget (have working PoC for this approach).

Regarding constructor injection, it seems like CXF does not support passing the arguments to provider constructor (in case of CDI, w/o @Context annotation) so I it would be another (separate) issue to look at.

Can someone help me point out what is this simpler approach is all about:

Simpler (interim?) possible solution would be to complement the CDI injection with @Context injection (delegating this work to the CXF as it works right now for non-proxy beans and non-CDI deployments). This could be done by observing ProcessInjectionTarget events and supplying our own InjectionTarget (have working PoC for this approach)

Or is there any other way, where Spring Framework can inject the ResourceInfo object in a correct way?

I have registered the filters like this in my applicationContext.xml :

<jaxrs:server id="endpoints">
    <jaxrs:providers>
            <ref bean="authenticationFilter" />
            <ref bean="authorizationFilter" />
        </jaxrs:providers>
    </jaxrs:server>

Solution

  • The problem is the @PreMatching annotation.

    Global binding annotation that can be applied to a container request filter to indicate that such filter should be applied globally on all resources in the application before the actual resource matching occurs.

    The key is highlighted part "before the actual resource matching occurs". So even before the resource is matched to the request, the filter is called. And if there is no resource matched yet, then it is impossible to get any information from the ResourceInfo. If you just remove the @PreMatching annotation, then the filter will be called after a matching resource is found, and you will be able to access all the info from the ResourceInfo.