Обратный прокси-сервер Java http с переписыванием URL-адреса ответа HTML - PullRequest
0 голосов
/ 14 мая 2018

Я хочу, чтобы мое веб-приложение на Java поддерживало прокси-контент, предоставляемый другим веб-сервером.

http://myapp.com/proxy/* -> http://other_app_to_proxy.com:9090/

Мое приложение будет обрабатывать все вопросы, связанные с аутентификацией, и обслуживать другие приложения в подпуть в том же домене.

Я нашел, как сделать обратный прокси: HTTP-Proxy-Servlet .

Проблема в том, что у другого приложения есть абсолютные URL-адреса, такие как /css/style.css, и когда страница открывается в моем приложении, этот URL-адрес становится недоступным, так как в моей настройке это должно быть /proxy/css/style.css.

Я понял, что мне нужен какой-то фильтр перезаписи URL, который будет изменять исходящий ответ, отправляемый клиенту. Я пытался изучить Tuckey UrlRewrite , но похоже, что он предназначен для другой цели - в нем есть множество инструментов для изменения входящего URL-адреса и перенаправления запросов в другие места.

Может ли кто-нибудь указать мне какое-нибудь решение?

Ответы [ 3 ]

0 голосов
/ 04 июня 2018

Я подошел к нескольким классам, которые позволяют полностью переписывать тело ответа в фильтре.

Пара базовых классов:

import javax.servlet.*;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.PrintWriter;

public abstract class AbstractResponseAlteringFilter implements Filter {

    public void init(FilterConfig fConfig) throws ServletException {
    }

    protected static class ByteArrayServletStream extends ServletOutputStream {
        ByteArrayOutputStream baos;

        ByteArrayServletStream(ByteArrayOutputStream baos) {
            this.baos = baos;
        }

        public void write(int param) throws IOException {
            baos.write(param);
        }
    }

    protected static class ByteArrayPrintWriter {

        private ByteArrayOutputStream baos = new ByteArrayOutputStream();

        private PrintWriter pw = new PrintWriter(baos);

        private ServletOutputStream sos = new ByteArrayServletStream(baos);

        public PrintWriter getWriter() {
            return pw;
        }

        public ServletOutputStream getStream() {
            return sos;
        }

        byte[] toByteArray() {
            return baos.toByteArray();
        }
    }

    protected static class CharResponseWrapper extends HttpServletResponseWrapper {
        private ByteArrayPrintWriter output;
        private boolean usingWriter;

        public CharResponseWrapper(HttpServletResponse response) {
            super(response);
            usingWriter = false;
            output = new ByteArrayPrintWriter();
        }

        public byte[] getByteArray() {
            return output.toByteArray();
        }

        @Override
        public ServletOutputStream getOutputStream() throws IOException {
            // will error out, if in use
            if (usingWriter) {
                super.getOutputStream();
            }
            usingWriter = true;
            return output.getStream();
        }

        @Override
        public PrintWriter getWriter() throws IOException {
            // will error out, if in use
            if (usingWriter) {
                super.getWriter();
            }
            usingWriter = true;
            return output.getWriter();
        }

        public String toString() {
            return output.toString();
        }
    }

    public void destroy() {
    }
}



import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.OutputStream;

public abstract class AbstractResponseBodyAlteringFilter extends AbstractResponseAlteringFilter {

    private final static Logger logger = LoggerFactory.getLogger(AbstractResponseBodyAlteringFilter.class);


    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException,
            ServletException {
        HttpServletResponse httpResponse = (HttpServletResponse) response;

        CharResponseWrapper wrappedResponse = new CharResponseWrapper(httpResponse);
        chain.doFilter(request, wrappedResponse);

        logger.info("wrappedResponse.getContentType() = {}", wrappedResponse.getContentType());

        byte[] responseBytes = wrappedResponse.getByteArray();
        if (StringUtils.containsAny(wrappedResponse.getContentType(), "text/", "javascript")) {
            responseBytes = modifyResponseBody(new String(responseBytes, "UTF-8")).getBytes("UTF-8");
        }
        OutputStream out = httpResponse.getOutputStream();

        logger.info("wrappedResponse.getStatus() = {}", wrappedResponse.getStatus());

        httpResponse.setStatus(wrappedResponse.getStatus());
        httpResponse.setContentType(wrappedResponse.getContentType());

        httpResponse.setContentLength(responseBytes.length);
        out.write(responseBytes);
        out.flush();
        httpResponse.flushBuffer();
    }

    protected abstract String modifyResponseBody(String body);

}

А вот конечный пользовательский класс:

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;

public class ProxyResponseBodyRewriteFilter extends AbstractResponseBodyAlteringFilter {

    @Value("${proxy.filter.prefix:/proxy/}")
    String prefix;

    private final static Logger logger = LoggerFactory.getLogger(ProxyResponseBodyRewriteFilter.class);

    protected String modifyResponseBody(String body) {
        body = body.replaceAll("href\\s*=\\s*\"\\s*/", "href=\"" + prefix);
        body = body.replaceAll("src\\s*=\\s*\"\\s*/", "src=\"" + prefix);
        body = body.replace("</head>", "<base href=\"/proxy/\"></head>");
        return body;
    }

}
0 голосов
/ 26 февраля 2019

Я сталкиваюсь с подобной проблемой, желая использовать ссылочный HttpProxyServlet, но мне также нужно изменить URL-адреса, встроенные в ответ.Я сделал следующее, как часть подкласса ProxyServlet, и он работает (хотя код все еще немного наклонен к моему сценарию использования и не является полностью универсальным), но мне любопытно, если вы когда-нибудь нашли что-то более простое / уже написанное, @snowindy?

public class CustomProxyServlet extends ProxyServlet {

    @Override
    protected void copyResponseEntity(
            HttpResponse proxyResponse, HttpServletResponse servletResponse,
            HttpRequest proxyRequest, HttpServletRequest servletRequest
        ) throws IOException {

        HttpEntity entity = proxyResponse.getEntity();
        if (entity != null) {
            OutputStream servletOutputStream = servletResponse.getOutputStream();
            if (isRewritable(proxyResponse)) {
                RewriteOutputStream rewriter = null;
                try {
                    rewriter = new RewriteOutputStream(servletOutputStream);
                    entity.writeTo(rewriter);
                }
                finally { rewriter.flush(); }
            }
            else {
                // parent's default behavior
                entity.writeTo(servletOutputStream);
            }
        }
    }

    private boolean isRewritable(HttpResponse httpResponse) {
        boolean rewriteable = false;
        Header[] contentTypeHeaders = httpResponse.getHeaders("Content-Type");
        for (Header header : contentTypeHeaders) {
            // May need to accept other types
            if (header.getValue().contains("html")) rewriteable = true;
        }
        return rewriteable;
    }
}

где RewriteOutputStream равен

public class RewriteOutputStream extends FilterOutputStream {
    // Provided implementation based on BufferedOutputStream adding
    // config-based string replacement before writing to output.
}
0 голосов
/ 14 мая 2018

Попробуйте изменить местоположение / прокси на / proxy / (конечный слеш) в файле конфигурации сервера вашего обратного прокси.

См. Ссылку, если вы используете nginx в качестве обратного прокси-сервера.

https://docs.nginx.com/nginx/admin-guide/web-server/reverse-proxy/

...