Подтвердить что ты не робот

Spring mvc rest service redirect/forward/proxy

Я создал веб-приложение с использованием Spring MVC Framework для публикации сервисов REST. Например:

@Controller
@RequestMapping("/movie")
public class MovieController {

@RequestMapping(value = "/{id}", method = RequestMethod.GET)
public @ResponseBody Movie getMovie(@PathVariable String id, @RequestBody user) {

    return dataProvider.getMovieById(user,id);

}

Теперь мне нужно развернуть приложение, но у меня возникла следующая проблема: клиенты не имеют прямого доступа к компьютеру, на котором находится приложение (имеется брандмауэр). Поэтому мне нужен слой перенаправления на прокси-машине (доступной для клиентов), которая вызывает реальный сервис отдыха.

Я попытался сделать новый вызов, используя RestTemplate: Например:

@Controller
@RequestMapping("/movieProxy")
public class MovieProxyController {

    private String address= "http://xxx.xxx.xxx.xxx:xx/MyApp";

    @RequestMapping(value = "/{id}", method = RequestMethod.GET)
    public @ResponseBody Movie getMovie(@PathVariable String id,@RequestBody user,final HttpServletResponse response,final HttpServletRequest request) {

        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        RestTemplate restTemplate = new RestTemplate();
        return restTemplate.exchange( address+ request.getPathInfo(), request.getMethod(), new HttpEntity<T>(user, headers), Movie.class);

}

Это нормально, но мне нужно переписать каждый метод в контроллере, чтобы использовать шаблон рестайта. Кроме того, это вызывает избыточную сериализацию/десериализацию на прокси-машине.

Я попытался написать обобщенную функцию, используя restemplate, но это не сработало:

@Controller
@RequestMapping("/movieProxy")
public class MovieProxyController {

    private String address= "http://xxx.xxx.xxx.xxx:xx/MyApp";

    @RequestMapping(value = "/**")
    public ? redirect(final HttpServletResponse response,final HttpServletRequest request) {        
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        RestTemplate restTemplate = new RestTemplate();
        return restTemplate.exchange( address+ request.getPathInfo(), request.getMethod(), ? , ?);

}

Я не смог найти метод resttemplate, который работает с объектами запроса и ответа.

Я также пытался spring перенаправить и вперед. Но редирект не меняет IP-адрес клиента запроса, поэтому я думаю, что в этом случае он бесполезен. Я также не смог переслать на другой URL.

Есть ли более подходящий способ добиться этого? Заранее спасибо.

4b9b3361

Ответ 1

Вы можете отразить/прокси все запросы с этим:

private String server = "localhost";
private int port = 8080;

@RequestMapping("/**")
@ResponseBody
public String mirrorRest(@RequestBody String body, HttpMethod method, HttpServletRequest request) throws URISyntaxException
{
    URI uri = new URI("http", null, server, port, request.getRequestURI(), request.getQueryString(), null);

    ResponseEntity<String> responseEntity =
        restTemplate.exchange(uri, method, new HttpEntity<String>(body), String.class);

    return responseEntity.getBody();
}

Это не будет отражать никаких заголовков.

Ответ 2

Вы можете использовать Netflix Zuul для маршрутизации запросов, поступающих в весеннее приложение, в другое весеннее приложение.

Допустим, у вас есть два приложения: 1.songs-app, 2.api-gateway

В приложении api-gateway сначала добавьте зависимость zuul, затем вы можете просто определить правило маршрутизации в application.yml следующим образом:

pom.xml

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-zuul</artifactId>
    <version>LATEST</version>
</dependency>

application.yml

server:
  port: 8080
zuul:
  routes:
    foos:
      path: /api/songs/**
      url: http://localhost:8081/songs/

и, наконец, запустите приложение api-gateway, например:

@EnableZuulProxy
@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

Теперь шлюз будет перенаправлять все запросы /api/songs/ на http://localhost:8081/songs/.

Рабочий пример здесь: https://github.com/muatik/spring-playground/tree/master/spring-api-gateway

Другой ресурс: http://www.baeldung.com/spring-rest-with-zuul-proxy

Ответ 3

Вот мой модифицированный вариант оригинального ответа, который отличается четырьмя пунктами:

  1. Это не делает тело запроса обязательным, и, как таковое, не допускает сбой запросов GET.
  2. Копирует все заголовки, присутствующие в исходном запросе. Если вы используете другой прокси/веб-сервер, это может вызвать проблемы из-за длины содержимого/сжатия gzip. Ограничьте заголовки теми, которые вам действительно нужны.
  3. Он не перекодирует параметры запроса или путь. Мы ожидаем, что они все равно будут закодированы. Обратите внимание, что другие части вашего URL также могут быть закодированы. Если это так, используйте весь потенциал UriComponentsBuilder.
  4. Он возвращает коды ошибок с сервера правильно.

@RequestMapping("/**")
public ResponseEntity mirrorRest(@RequestBody(required = false) String body, 
    HttpMethod method, HttpServletRequest request, HttpServletResponse response) 
    throws URISyntaxException {
    String requestUrl = request.getRequestURI();

    URI uri = new URI("http", null, server, port, null, null, null);
    uri = UriComponentsBuilder.fromUri(uri)
                              .path(requestUrl)
                              .query(request.getQueryString())
                              .build(true).toUri();

    HttpHeaders headers = new HttpHeaders();
    Enumeration<String> headerNames = request.getHeaderNames();
    while (headerNames.hasMoreElements()) {
        String headerName = headerNames.nextElement();
        headers.set(headerName, request.getHeader(headerName));
    }

    HttpEntity<String> httpEntity = new HttpEntity<>(body, headers);
    RestTemplate restTemplate = new RestTemplate();
    try {
        return restTemplate.exchange(uri, method, httpEntity, String.class);
    } catch(HttpStatusCodeException e) {
        return ResponseEntity.status(e.getRawStatusCode())
                             .headers(e.getResponseHeaders())
                             .body(e.getResponseBodyAsString());
    }
}

Ответ 4

Если вам удастся использовать решение более низкого уровня, например mod_proxy, это будет более простой способ, но если вам нужно больше контроля (например, безопасность, перевод, бизнес-логика), вы можете взглянуть на Apache Верблюд: http://camel.apache.org/how-to-use-camel-as-a-http-proxy-between-a-client-and-server.html

Ответ 5

Вам нужен что-то вроде jetty transparent proxy, который фактически перенаправит ваш вызов, и вы получите возможность перезаписать запрос, если вам это нужно. Вы можете получить его подробную информацию на http://reanimatter.com/2016/01/25/embedded-jetty-as-http-proxy/

Ответ 6

прокси-контроллер с oauth2

@RequestMapping("v9")
@RestController
@EnableConfigurationProperties
public class ProxyRestController {
    Logger logger = LoggerFactory.getLogger(this.getClass());

    @Autowired
    OAuth2ProtectedResourceDetails oAuth2ProtectedResourceDetails;

    @Autowired
    private ClientCredentialsResourceDetails clientCredentialsResourceDetails;

    @Autowired
    OAuth2RestTemplate oAuth2RestTemplate;


    @Value("${gateway.url:http://gateway/}")
    String gatewayUrl;

    @RequestMapping(value = "/proxy/**")
    public String proxy(@RequestBody(required = false) String body, HttpMethod method, HttpServletRequest request, HttpServletResponse response,
                        @RequestHeader HttpHeaders headers) throws ServletException, IOException, URISyntaxException {

        body = body == null ? "" : body;
        String path = request.getRequestURI();
        String query = request.getQueryString();
        path = path.replaceAll(".*/v9/proxy", "");
        StringBuffer urlBuilder = new StringBuffer(gatewayUrl);
        if (path != null) {
            urlBuilder.append(path);
        }
        if (query != null) {
            urlBuilder.append('?');
            urlBuilder.append(query);
        }
        URI url = new URI(urlBuilder.toString());
        if (logger.isInfoEnabled()) {
            logger.info("url: {} ", url);
            logger.info("method: {} ", method);
            logger.info("body: {} ", body);
            logger.info("headers: {} ", headers);
        }
        ResponseEntity<String> responseEntity
                = oAuth2RestTemplate.exchange(url, method, new HttpEntity<String>(body, headers), String.class);
        return responseEntity.getBody();
    }


    @Bean
    @ConfigurationProperties("security.oauth2.client")
    @ConditionalOnMissingBean(ClientCredentialsResourceDetails.class)
    public ClientCredentialsResourceDetails clientCredentialsResourceDetails() {
        return new ClientCredentialsResourceDetails();
    }

    @Bean
    @ConditionalOnMissingBean
    public OAuth2RestTemplate oAuth2RestTemplate() {
        return new OAuth2RestTemplate(clientCredentialsResourceDetails);
    }