如何让 Jersey 使用 HttpServletResponse 包装器调用资源方法?

How do I get Jersey to call a resource method with an HttpServletResponse wrapper?

我正在尝试系统地解决 HTTP 响应拆分问题。我为 HttpServletResponse 开发了一个名为 HardenedHttpServletResponse 的包装器 class 来减轻拆分尝试。

遗憾的是,我无法让 Jersey 使用我的 HardenedHttpServletResponse 调用我的资源方法。我尝试时得到 nulls。

这是一个带有 HTTP 响应拆分漏洞的人为 JAX-RS 资源,可以通过将 percent-encoded CRLF (%0d%0a) 放入 文件名查询参数:

AttachmentResource.java:

import javax.servlet.http.HttpServletResponse;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;

@Path("/attachment")
@Produces(MediaType.APPLICATION_JSON)
public final class AttachmentResource {
    @GET
    @Path("/file")
    public StreamingOutput getAttachment(
        @Context HttpServletResponse response,
        @QueryParam("filename") String filename
    ) throws Exception {
        response.setHeader(
            "content-disposition",
            "attachment; filename=" + filename
        );
        return new DummyStreamingOutput();
    }
}

这里是 StreamingOutput 的虚拟实现,使其成为一个完整的示例:

DummyStreamingOutput.java:

import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.StreamingOutput;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;

private static DummyFileStreamingOutput implements StreamingOutput {
    @Override
    public void write(OutputStream outputStream) throws IOException, WebApplicationException {
        String message = "Hello, World!";
        byte[] bytes = message.getBytes(StandardCharsets.UTF_8);
        outputStream.write(bytes);
        outputStream.flush();
        outputStream.close();
    }
}

这里是 HttpServletResponse 包装器 class 如果检测到 header 名称或值中的 CR 或 LF 字符,它会通过抛出异常来减轻 HTTP 响应拆分:

HardenedHttpServletResponse.java:

import javax.inject.Inject;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;
import javax.ws.rs.core.Context;

final class HardenedHttpServletResponse extends HttpServletResponseWrapper {
    @Inject
    HardenedHttpServletResponse(@Context HttpServletResponse response) {
        super(response);
    }

    @Override
    public void setHeader(String name, String value) {
        mitigateResponseSplitting(name);
        mitigateResponseSplitting(value);
        super.setHeader(name, value);
    }

    @Override
    public void addHeader(String name, String value) {
        mitigateResponseSplitting(name);
        mitigateResponseSplitting(value);
        super.setHeader(name, value);
    }

    @Override
    public void setIntHeader(String name, int value) {
        mitigateResponseSplitting(name);
        super.setIntHeader(name, value);
    }

    @Override
    public void setDateHeader(String name, long date) {
        mitigateResponseSplitting(name);
        super.setDateHeader(name, date);
    }

    private void mitigateResponseSplitting(String value) {
        if (value != null && (value.contains("\r") || value.contains("\n"))) {
            throw new HttpResponseSplittingException();
        }
    }
}

如果 response 参数的类型为 @Context HttpServletResponse,则 Jersey 提供实际响应 object,但是 null 如果 response 参数的类型为 @Context HardenedHttpServletResponse.

如何让 Jersey 使用 HttpServletResponse 包装器调用资源方法?

您可以通过将其添加到 DI 系统使其可注入。

resourceConfig.register(new AbstractBinder() {
    @Override
    public void configure() {
        bindAsContract(HardenedHttpServletResponse.class)
            .proxy(false)
            .proxyForSameScope(false)
            .in(RequestScoped.class);
    }
});

您将需要创建 class public 及其构造函数 public,以便 DI 系统可以创建它。这将允许您注入 HardenedHttpServletResponse

另请参阅:

  • Dependency injection with Jersey 2.0