ContainerRequestFilter在JAX-RS服务方法中导致NPE

时间:2014-07-23 08:40:46

标签: java java-ee jaxb jax-rs wildfly

我有一个小过滤器来使用一些非常基本的HMAC方法来验证请求。

只要没有实际的请求有效载荷,一切正常。

现在,当存在非空的有效负载时,身份验证本身仍然有效,但负责该请求的REST服务不会收到任何内容。 这里是过滤方法:

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

这种无参数服务方法有效:

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

这个人不会收到任何东西:

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

请注意,参数是简单的JAXB注释pojos。出于某种原因,这里没有任何内容可以解组。

所有东西都在Wildfly 8上运行,而我正在使用服务器库(Resteasy,jackson等)。

我希望你们能给我一个提示......谢谢!

3 个答案:

答案 0 :(得分:2)

以前的评论是对的,但使用会更好

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

因为流将被关闭,而jackson将不会从端点上的流中获取实体。 在创建流之前使用jackson ObjectMapper,如果没有这个,你就无法在端点上获取实体简单Stream使用不适用于jackson的本机序列化

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

答案 1 :(得分:1)

在过滤器中消耗了获取完整实体所需的ContainerRequestContext中的inputStream。每个后续组件都不会从流中获得任何内容。

有几种可能的解决方案。我决定引入一个缓冲区:

// ...
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);
// ....

我不知道是否有更好的方法可以解决这个问题......这个方法有效。

答案 2 :(得分:1)

这里很晚,但是当我遇到相同的问题时,这对我有用:

您只需要从InputStream类型实体中创建一个String类型,然后像这样将其写回到您的ContainerRequestContext-

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

就在您的代码下面

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

IOUtils来自org.apache.commons.io