Cet article est un article relais de 2018 Calendrier de l'Avent de Link Information System .. Relayé par un membre du groupe de engineer.hanzomon. (Cliquez ici pour le lien système d'information Facebook (https://ja-jp.facebook.com/lis.co.jp/))
Je m'appelle @shinevillage et je suis en charge du 13e jour du calendrier de l'Avent. Je suis principalement en charge du développement d'applications web utilisant Java. Cette fois, lorsque j'étais en charge d'un certain travail, je parlerai de la création d'un proxy inverse en Java comme outil de test.
Le terme «proxy inverse» est plus familier aux spécialistes de l'infrastructure, mais il peut parfois être également recherché par les développeurs Web. (Pour ceux qui demandent "Qu'est-ce qu'un proxy inverse en premier lieu?")
Dans mon cas, lors du test d'une application avec la configuration suivante, un problème interdomaine est survenu et je le voulais.
Il semble qu'utiliser Apache ou Nginx soit le moyen le plus populaire de créer un proxy inverse, mais dans mon cas, malheureusement ** "L'installation de logiciels libres exe est interdite. Les scripts que j'ai écrits sont ok. Je travaille dans un environnement comme "Pardonnez-moi" **, et je ne pouvais pas utiliser le logiciel ci-dessus, j'ai donc écrit un proxy inverse pour les tests en Java, le langage dans lequel l'application est écrite.
--Java 1.8 (Raison de la sélection: Parce qu'il est utilisé au travail)
Le servlet de proxy HTTP de Smiley fournit un servlet (ProxyServlet) qui agit comme un serveur proxy. Vous pouvez utiliser ** URITemplateProxyServlet ** pour contrôler dynamiquement la destination de transfert (nom d'hôte, numéro de port, chemin de contexte) avec la chaîne de requête. Le comportement du proxy inverse est reproduit en définissant la chaîne de caractères de requête en fonction de l'URL demandée avec le filtre de servlet.
Construisez avec Maven.
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>
Vient ensuite la classe principale. Enregistrez le filtre de servlet et le ProxyServlet ici.
Main.java
@SpringBootApplication
public class Main extends SpringBootServletInitializer implements WebApplicationInitializer {
/**
*Point d'entrée lors du démarrage indépendant.
*/
public static void main(String[] args) throws Throwable {
SpringApplicationBuilder builder = new SpringApplicationBuilder(Main.class);
builder.run();
}
/**
*Point d'entrée lors du démarrage du conteneur de servlet.
*/
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(Main.class);
}
/**
* {@see org.springframework.web.filter.HiddenHttpMethodFilter}Désactiver.
*
*URITemplateProxyServlet est{@link ServletRequest#getInputStream}Utiliser
*Lorsque le paramètre de requête est accédé côté filtre, le côté servlet de processus
*Les paramètres de demande ne peuvent pas être acquis.
*Par conséquent, dans cet outil, la fonction fournie par HiddenHttpMethodFilter n'est pas nécessaire, le filtre est donc désactivé.
*/
@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}Désactiver.
*
*URITemplateProxyServlet est{@link ServletRequest#getInputStream}Utiliser
*Lorsque le paramètre de demande est accédé côté filtre, le côté servlet
*Les paramètres de demande ne peuvent pas être acquis.
*Par conséquent, dans cet outil, la fonction fournie par HttpPutFormContentFilter n'est pas nécessaire, le filtre est donc désactivé.
*/
@Bean
public FilterRegistrationBean httpPutFormContentFilterRegistration(HttpPutFormContentFilter filter) {
FilterRegistrationBean registration = new FilterRegistrationBean(filter);
registration.setFilter(filter);
registration.setEnabled(false);
return registration;
}
/**
*Enregistrement du filtre de servlet pour l'application Web 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;
}
/**
*Enregistrement du filtre de servlet pour l'application Web 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;
}
/**
*Enregistrement du servlet proxy.
* @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;
}
}
Vient ensuite le filtre de servlet. Le comportement principal est défini comme une classe abstraite et le traitement qui dépend de la destination de transfert du côté de la sous-classe est décrit.
AbstractRiverseProxyFilter.java
/**
*Filtre proxy inverse.
* {@see org.mitre.dsmiley.httpproxy.URITemplateProxyServlet}À
*Personnalisez la demande et la réponse pour fonctionner comme un proxy inverse.
*/
abstract public class AbstractRiverseProxyFilter implements Filter {
/**
*Obtenez l'hôte de destination du transfert
*/
abstract protected String getTransfarHost();
/**
*Obtenir la racine du contexte de transfert
*/
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}À
//Ajoutez le paramètre pour spécifier la destination du transfert à la chaîne de caractères de la requête
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()));
//Empêche le codage par défaut du conteneur de servlet d'être défini
_response.setCharacterEncoding(null);
//Couvrir l'objet de requête avec un wrapper et le transmettre au servlet proxy
RequestRewriteWrapper wrapRequest
= new RequestRewriteWrapper(request, routingQuery.toString());
filterChain.doFilter(wrapRequest, _response);
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {}
@Override
public void destroy() {}
/**
*Requête wrapper pour réécrire les chaînes de requête
*/
private static class RequestRewriteWrapper extends HttpServletRequestWrapper {
private final String queryString;
public RequestRewriteWrapper(HttpServletRequest request, String query) {
super(request);
this.queryString = query;
}
/**
* {@link HttpServletRequest#getQueryString()}Wrapper.
*Renvoie une chaîne de requête avec la destination de transfert ajoutée.
*/
@Override
public String getQueryString() {
return this.queryString;
}
}
}
AAppRiverseProxyFilter.java
/**
*Filtre de proxy inverse pour l'application Web A.
*/
public class AAppRiverseProxyFilter extends AbstractRiverseProxyFilter {
@Override
protected String getTransfarHost() {
return "xxx.xxx.xxx.1";
}
@Override
protected String getTransfarContextRoot() {
return "webapp-a";
}
}
BAppRiverseProxyFilter.java
/**
*Filtre de proxy inverse pour l'application Web B.
*/
public class BAppRiverseProxyFilter extends AbstractRiverseProxyFilter {
@Override
protected String getTransfarHost() {
return "xxx.xxx.xxx.2";
}
@Override
protected String getTransfarContextRoot() {
return "webapp-b";
}
}
Commencez par la commande suivante.
$ mvn spring-boot:run
Après avoir démarré l'outil, accédez à l'application Web A avec "http : // localhost: 8080 / webapp-a /" depuis le navigateur. Vous pourrez accéder à l'application Web B avec "http : // localhost: 8080 / webapp-b /".
Le contenu de cet article n'est qu'un extrait de la partie de base de l'outil créé.
Lors de la création d'un outil, les informations de destination de transfert ne sont pas écrites dans le code source comme dans l'exemple ci-dessus, mais
@value
C'est une bonne idée d'utiliser un mécanisme tel qu'une annotation pour la récupérer.
Même si j'ai attrapé le net, il n'y avait pas beaucoup d'articles écrivant un proxy inverse en Java, donc je l'ai écrit cette fois. Le proxy inverse de cet article n'est qu'un ** "outil de test" **, utilisez donc un produit décent pour votre environnement de production. <(_ _)>
Demain, c'est le 14e jour. Ceci est un article de @modest.
Recommended Posts