简体   繁体   中英

ContainerRequestFilter causes NPE in JAX-RS service method

I have a little filter to authenticate requests with some really basic HMAC-approach.

Everything worked fine as long as there there was no actual request payload.

Now, when there is a non-empty payload the authentication itself still works but the REST-service which is responsible for the request won't receive anything. Here the filter method:

public void filter(ContainerRequestContext context)
            throws IOException {

        // perform check on responsible service - look for @PermitAll and so on
        if (isAuthenticationRequired(context) == false) {
            return;
        }


        String callerKey = context.getHeaderString("callerKey");

        if (callerKey == null) {
            context.abortWith(Response.
                    status(Response.Status.UNAUTHORIZED)
                    .build());
        }

        UriInfo uriInfo = context.getUriInfo();            
        String absPath = uriInfo.getAbsolutePath().toString();   

        String checksum = context.getHeaderString("checksum");

        // As this is the only action related to the request's entity (payload)
        // I already played around with it a while. This is a readonly operation, isn't 
        // it?! The stream must not be closed.
        InputStream entityStream = context.getEntityStream();
        Scanner scanner = new Scanner(entityStream).useDelimiter("\\A");
        String entity = (scanner.hasNext()) ? scanner.next() : "";

        // Collect parameters for underlying business bean that performs the check.   
        RequestAuthenticationOrder order;
        order = new RequestAuthenticationOrderBuilder()
                .absolutePath(absPath)
                .completeEntity(entity)
                .checksum(checksum)
                .callerlKey(callerKey)
                .build();
        try {
            // basically checks if checksum is correct and if that's true make 
            // corresponding user available to the REST-service
            context.setProperty("authenticatedUser",
                    identificationService.authenticateRequest(order));

        } catch (AuthenticationException aux) {
            Logger.getLogger(AuthenticationFilter.class.getName()).log(Level.SEVERE, null, aux);
        } 
    }

This no argument service method works:

@Path("/protected")
public class ProtectedController extends AbstractResourceController {

    @GET
    @Produces(MediaType.APPLICATION_JSON)
    public Response protect () {
        // callingUser is the result of the previous filtering. It's defined in 
        // superclass AbstractResourceController
        return Response.accepted(new ProtectedResponse((callingUser != null)
                ? callingUser.getNickname() 
                : "no user authenticated?")).build();
    }
}

This one won't receive anything:

@Path("/logout")
public class LogoutController extends AbstractResourceController{
    @POST @Consumes(MediaType.APPLICATION_JSON)
    public void logout (LogoutRequest request) {
        request.getFoo(); // NPE       
    }
}

Note that the parameters are simple JAXB-annotated pojos. For some reason nothing gets unmarshalled here.

Everything's running on Wildfly 8 and i'm using the server libraries (Resteasy, jackson, and so on).

I hope you guys can give me a hint...Thank you!

Previous comment is right but use will be better

ContainerRequest context = (ContainerRequest) containerRequestContext;
context.bufferEntity();
String entity = context.readEntity(String.class);

because stream will be closed and jackson will not get entity from stream on endpoint. and before creating stream use jackson ObjectMapper without this u can't get entity on endpoint simple Stream use native serialization that isn't applicable for jackson

requestContext.setEntityStream(new ByteArrayInputStream(new ObjectMapper().writeValueAsBytes(entity)));

The inputStream from ContainerRequestContext that is needed to fetch the complete entity gets consumed in the filter. Every subsequent component won't get anything off the stream.

There are several possible solutions. I decided to introduce a buffer:

// ...
InputStream entityStream = new BufferedInputStream(context.getEntityStream());
entityStream.mark(1000); // Hmn, what would be a proper limit...Further investigation
Scanner scanner = new Scanner(entityStream).useDelimiter("\\A");
String entity = (scanner.hasNext()) ? scanner.next() : "";
entityStream.reset();
context.setEntityStream(entityStream);
// ....

I don't know if there are better ways of solving this...This one works.

Quite late here but this worked for me when I faced the same issue :

You just need to create a InputStream type from your String type entity and write it back to your ContainerRequestContext like this --

requestContext.setEntityStream(IOUtils.toInputStream(entity));

just below you code

String entity = (scanner.hasNext()) ? scanner.next() : "";

IOUtils is from org.apache.commons.io .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM