resttemplate로 proxy controller 구현하기

h블로그·2023년 5월 28일

보안상의 문제로 기존에 존재하는 정해진 api 서버로 bypass 하는 서비스를 구현해야 했다.
지금은 bypass 하지만 시간이 지나서 앞에 bypass하는 서비스가 없어질수도 있단 생각을 하면서 방법을 찾아보았다.

간단하게 구현하기 위해서 resttemplate로 요청을 그대로 다음 서비스로 넘겨주게 구현했다.

proxy controller

@RequestMapping("/proxy/**")
@RequiredArgsConstructor
public class ProxyController {
    private final HttpApiRequest httpApiRequest;


    @GetMapping
    public ResponseEntity<ApiResponseModel> getProxy(HttpServletRequest request, HttpServletResponse response,
                                           @RequestBody(required = false) Object body) throws URISyntaxException {
        return ResponseEntity.ok(ApiResponseModel.success(httpApiRequest.exchange(request, body).getBody()));
    }

    @PostMapping
    public ResponseEntity<ApiResponseModel> postProxy(HttpServletRequest request, HttpServletResponse response,
                                            @RequestBody(required = false) Object body) throws URISyntaxException {
        return ResponseEntity.ok(ApiResponseModel.success(httpApiRequest.exchange(request, body).getBody()));
    }

    @PutMapping
    public ResponseEntity<ApiResponseModel> putProxy(HttpServletRequest request, HttpServletResponse response,
                                            @RequestBody(required = false) Object body) throws URISyntaxException {
        return ResponseEntity.ok(ApiResponseModel.success(httpApiRequest.exchange(request, body).getBody()));
    }

    @DeleteMapping
    public ResponseEntity<ApiResponseModel> deleteProxy(HttpServletRequest request, HttpServletResponse response,
                                           @RequestBody(required = false) Object body) throws URISyntaxException {
        return ResponseEntity.ok(ApiResponseModel.success(httpApiRequest.exchange(request, body).getBody()));
    }
}
  • get, post, put, delete mapping까지 넘겨줄 메소드를 만들었다.
  • 처음엔 RequestBody의 타입을 byte[]로 해서 json 구조가 아닌 요청도 proxy 서비스의 변경없이 처리할 수 있도록 구현했다. 하지만 resttemplate.exchange() 메소드로 넘겼을 때, 예상과 달리 처리하지 못해서 현재 서비스에서는 json 구조외에 없을것으로 예상하고 Object타입을 body로 받게 수정했다.

resttemplate

httpApiRequest.java

public ResponseEntity<?> exchange(HttpServletRequest request,
                                           @RequestBody(required = false) Object body) throws URISyntaxException {
        String originReqURL = request.getRequestURI().replaceAll("^/la-lms", "");
        String originQueryString = request.getQueryString();
        String urlStr = LMS_API_DOMAIN_NAME + originReqURL + (StringUtils.isEmpty(originQueryString) ? "" : "?"+originQueryString);

        URI url = new URI(urlStr);

        // header
        Enumeration<String> headerNames = request.getHeaderNames();
        MultiValueMap<String, String> headers = new LinkedMultiValueMap<>();
        while(headerNames.hasMoreElements()) {
            String headerName = headerNames.nextElement();

            if (headerName.equalsIgnoreCase("host")) {	// host 정보는 header에서 제외
                continue;
            }

            headers.add(headerName, request.getHeader(headerName));
        }

        // http entity (body, header)
        HttpEntity<Object> httpEntity = new HttpEntity<>(body, headers);

        return restTemplate.exchange(url, HttpMethod.valueOf(request.getMethod()), httpEntity, Object.class);
    }
  • header에 어떤 요청을 넘기던 그대로 넘기도록 했다. 처음에 host 정보까지 그대로 넘겨서 개발서버에 배포하였을때 자기 스스로를 계속 호출하는 버그를 발생시켜서 원인을 찾느라 애를 먹었었다.
profile
😎🙈🙈🙈🤓

0개의 댓글