Dieser Artikel ist ein Relaisartikel von 2018 Adventskalender von Link Information System. .. Weitergeleitet von einem Gruppenmitglied von engineer.hanzomon. (Klicken Sie hier für das Link-Informationssystem Facebook) (https://ja-jp.facebook.com/lis.co.jp/)
Mein Name ist @shinevillage und ich bin verantwortlich für den 13. Tag des Adventskalenders. Ich bin hauptsächlich für die Entwicklung von Webanwendungen mit Java verantwortlich. Dieses Mal, als ich für einen bestimmten Job verantwortlich war, werde ich darüber sprechen, einen Reverse-Proxy in Java als Testwerkzeug zu erstellen.
Der Begriff "Reverse Proxy" ist den Infrastrukturmitarbeitern vertrauter, kann aber manchmal auch von Webentwicklern gewünscht werden. (Wenn Sie fragen "Was ist überhaupt ein Reverse-Proxy?")
In meinem Fall ist beim Testen einer App mit der folgenden Konfiguration ein domänenübergreifendes Problem aufgetreten, und ich wollte es.
Es scheint, dass die Verwendung von Apache oder Nginx die beliebteste Methode zum Erstellen eines Reverse-Proxys ist, aber in meinem Fall leider ** "Die Installation von Exe für freie Software ist verboten. Von mir geschriebene Skripte sind in Ordnung. Ich arbeite in einer Umgebung wie "Ich werde dir vergeben" ** und konnte die oben genannte Software nicht verwenden. Deshalb habe ich einen Reverse-Proxy zum Testen in Java geschrieben, der Sprache, in der die App geschrieben ist.
Das HTTP-Proxyservlet von Smiley bietet ein Servlet (ProxyServlet), das als Proxyserver fungiert.
Mit ** URITemplateProxyServlet ** können Sie das Weiterleitungsziel (Hostname, Portnummer, Kontextpfad) mit der Abfragezeichenfolge dynamisch steuern.
Das Verhalten des Reverse-Proxys wird reproduziert, indem die Abfragezeichenfolge gemäß der angeforderten URL mit dem Servlet-Filter festgelegt wird.
Mit Maven bauen.
pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>riverse-proxy</groupId>
<artifactId>riverse-proxy</artifactId>
<version>1.0</version>
<packaging>war</packaging>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.14.RELEASE</version>
</parent>
<properties>
<project.build.sourceEncoding>Windows-31j</project.build.sourceEncoding>
<project.reporting.outputEncoding>Windows-31j</project.reporting.outputEncoding>
<maven.compile.source>1.8</maven.compile.source>
<maven.compile.target>1.8</maven.compile.target>
<java.version>1.8</java.version>
<spring.version>4.3.18.RELEASE</spring.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.mitre.dsmiley.httpproxy</groupId>
<artifactId>smiley-http-proxy-servlet</artifactId>
<version>1.10</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Als nächstes kommt die Hauptklasse. Registrieren Sie hier den Servlet-Filter und das ProxyServlet.
Main.java
@SpringBootApplication
public class Main extends SpringBootServletInitializer implements WebApplicationInitializer {
/**
*Einstiegspunkt beim selbständigen Starten.
*/
public static void main(String[] args) throws Throwable {
SpringApplicationBuilder builder = new SpringApplicationBuilder(Main.class);
builder.run();
}
/**
*Einstiegspunkt beim Starten des Servlet-Containers.
*/
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(Main.class);
}
/**
* {@see org.springframework.web.filter.HiddenHttpMethodFilter}Deaktivieren.
*
*URITemplateProxyServlet ist{@link ServletRequest#getInputStream}Benutzen
*Wenn auf den Anforderungsparameter auf der Filterseite zugegriffen wird, wird auf der Prozessservlet-Seite
*Anforderungsparameter können nicht erfasst werden.
*Daher ist in diesem Tool die von HiddenHttpMethodFilter bereitgestellte Funktion nicht erforderlich, sodass der Filter deaktiviert ist.
*/
@Bean
public FilterRegistrationBean hiddenHttpMethodFilterRegistration(HiddenHttpMethodFilter filter) {
FilterRegistrationBean registration = new FilterRegistrationBean(filter);
registration.setFilter(filter);
registration.setEnabled(false);
return registration;
}
/**
* {@see org.springframework.web.filter.HttpPutFormContentFilter}Deaktivieren.
*
*URITemplateProxyServlet ist{@link ServletRequest#getInputStream}Benutzen
*Wenn auf den Anforderungsparameter auf der Filterseite zugegriffen wird, wird auf der Proxy-Servlet-Seite
*Anforderungsparameter können nicht erfasst werden.
*Daher ist in diesem Tool die von HttpPutFormContentFilter bereitgestellte Funktion nicht erforderlich, sodass der Filter deaktiviert ist.
*/
@Bean
public FilterRegistrationBean httpPutFormContentFilterRegistration(HttpPutFormContentFilter filter) {
FilterRegistrationBean registration = new FilterRegistrationBean(filter);
registration.setFilter(filter);
registration.setEnabled(false);
return registration;
}
/**
*Registrierung des Servlet-Filters für die Webanwendung A..
*/
@Bean
public FilterRegistrationBean applicationARiverseProxyFilterRegistration() {
Filter filter = new AAppRiverseProxyFilter();
FilterRegistrationBean registration = new FilterRegistrationBean(filter);
registration.setOrder(FilterRegistrationBean.REQUEST_WRAPPER_FILTER_MAX_ORDER);
registration.setUrlPatterns(Arrays.asList("/webapp-a/*"));
registration.setDispatcherTypes(EnumSet.of(DispatcherType.REQUEST));
return registration;
}
/**
*Registrierung des Servlet-Filters für die Webanwendung B..
*/
@Bean
public FilterRegistrationBean applicationBRiverseProxyFilterRegistration() {
Filter filter = new BAppRiverseProxyFilter();
FilterRegistrationBean registration = new FilterRegistrationBean(filter);
registration.setOrder(FilterRegistrationBean.REQUEST_WRAPPER_FILTER_MAX_ORDER);
registration.setUrlPatterns(Arrays.asList("/webapp-b/*"));
registration.setDispatcherTypes(EnumSet.of(DispatcherType.REQUEST));
return registration;
}
/**
*Registrierung des Proxy-Servlets.
* @see https://github.com/mitre/HTTP-Proxy-Servlet
*/
@Bean
public ServletRegistrationBean riverseProxyServletRegistration() {
HttpServlet servlet = new URITemplateProxyServlet();
ServletRegistrationBean registration = new ServletRegistrationBean(servlet);
registration.addInitParameter("preserveHost", "true");
registration.addInitParameter("preserveCookies", "true");
registration.addInitParameter("http.protocol.handle-redirects", "true");
registration.addInitParameter("http.socket.timeout", "300000");
registration.addInitParameter("http.read.timeout", "300000");
registration.addInitParameter("targetUri", "http://{__proxyHost}/{__proxyContextRoot}");
registration.setUrlMappings(Arrays.asList("/webapp-a/*", "/webapp-b/*"));
return registration;
}
}
Als nächstes kommt der Servlet-Filter. Das Hauptverhalten wird als abstrakte Klasse definiert, und die Verarbeitung, die vom Übertragungsziel auf der Unterklassenseite abhängt, wird beschrieben.
AbstractRiverseProxyFilter.java
/**
*Reverse-Proxy-Filter.
* {@see org.mitre.dsmiley.httpproxy.URITemplateProxyServlet}Zu
*Passen Sie die Anforderung und Antwort so an, dass sie als Reverse-Proxy fungieren.
*/
abstract public class AbstractRiverseProxyFilter implements Filter {
/**
*Holen Sie sich den Übertragungsziel-Host
*/
abstract protected String getTransfarHost();
/**
*Forwarding Context Root
*/
abstract protected String getTransfarContextRoot();
@Override
public void doFilter(ServletRequest _request, ServletResponse _response,
FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) _request;
// {@see org.mitre.dsmiley.httpproxy.URITemplateProxyServlet}Zu
//Fügen Sie den Parameter hinzu, um das Übertragungsziel für die Abfragezeichenfolge anzugeben
StringBuilder routingQuery = new StringBuilder();
String query = request.getQueryString();
if (!StringUtils.isEmpty(query)) {
routingQuery.append(query);
routingQuery.append("&");
}
routingQuery.append(String.format("__proxyHost=%s&__proxyContextRoot=%s",
this.getTransfarHost(), this.getTransfarContextRoot()));
//Verhindert, dass die Standardcodierung des Servlet-Containers festgelegt wird
_response.setCharacterEncoding(null);
//Decken Sie das Anforderungsobjekt mit einem Wrapper ab und übergeben Sie es an das Proxy-Servlet
RequestRewriteWrapper wrapRequest
= new RequestRewriteWrapper(request, routingQuery.toString());
filterChain.doFilter(wrapRequest, _response);
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {}
@Override
public void destroy() {}
/**
*Request Wrapper zum Umschreiben von Abfragezeichenfolgen
*/
private static class RequestRewriteWrapper extends HttpServletRequestWrapper {
private final String queryString;
public RequestRewriteWrapper(HttpServletRequest request, String query) {
super(request);
this.queryString = query;
}
/**
* {@link HttpServletRequest#getQueryString()}Verpackung.
*Gibt eine Abfragezeichenfolge mit dem hinzugefügten Weiterleitungsziel zurück.
*/
@Override
public String getQueryString() {
return this.queryString;
}
}
}
AAppRiverseProxyFilter.java
/**
*Reverse-Proxy-Filter für Web-App A..
*/
public class AAppRiverseProxyFilter extends AbstractRiverseProxyFilter {
@Override
protected String getTransfarHost() {
return "xxx.xxx.xxx.1";
}
@Override
protected String getTransfarContextRoot() {
return "webapp-a";
}
}
BAppRiverseProxyFilter.java
/**
*Reverse-Proxy-Filter für Web-App B..
*/
public class BAppRiverseProxyFilter extends AbstractRiverseProxyFilter {
@Override
protected String getTransfarHost() {
return "xxx.xxx.xxx.2";
}
@Override
protected String getTransfarContextRoot() {
return "webapp-b";
}
}
Beginnen Sie mit dem folgenden Befehl.
$ mvn spring-boot:run
Wechseln Sie nach dem Starten des Tools im Browser zu Web App A mit "http : // localhost: 8080 / webapp-a /". Sie können auf Web App B mit "http : // localhost: 8080 / webapp-b /" zugreifen.
Der Inhalt dieses Artikels ist nur ein Auszug aus dem grundlegenden Teil des erstellten Tools.
Bei der eigentlichen Erstellung eines Tools werden die Informationen zum Übertragungsziel nicht wie im obigen Beispiel in den Quellcode geschrieben, sondern
@value
Es ist eine gute Idee, einen Mechanismus wie eine Anmerkung zum Abrufen zu verwenden.
Selbst wenn ich das Netz erwischt habe, gab es nicht viele Artikel, die Reverse Proxy in Java geschrieben haben, also habe ich es dieses Mal geschrieben. Der Reverse-Proxy in diesem Artikel ist nur ein ** "Test-Tool" **. Verwenden Sie daher ein anständiges Produkt für Ihre Produktionsumgebung. <(_ _)>
Morgen ist der 14. Tag. Dies ist ein Artikel von @modest.