SpringBoot利用RestTemplate实现反向代理
作者:风象南
反向代理是指以代理服务器接收客户端请求,然后将请求转发给内部服务器,并将内部服务器的响应返回给客户端,下面我们就来看看SpringBoot如何利用RestTemplate实现反向代理吧
之前发过一篇SpringBoot利用Undertow实现高可用的反向代理配置,有同学反馈tomcat用习惯了,担心切换Undertow有一定的风险。
今天分享另一种利用RestTemplate客户端工具来实现简单而高效的反向代理功能,底层无论使用哪种web服务均可使用。
什么是反向代理
反向代理是指以代理服务器接收客户端请求,然后将请求转发给内部服务器,并将内部服务器的响应返回给客户端。
客户端只与反向代理服务器通信,不直接访问内部服务器。
为什么选择RestTemplate实现反向代理
集成便捷:RestTemplate是Spring框架的核心组件,在SpringBoot项目中使用非常方便 功能丰富:支持各种HTTP方法、请求头处理、响应类型转换等 可定制性强:可以通过配置ClientHttpRequestFactory来自定义连接池、超时等参数 便于扩展:可以结合拦截器实现更复杂的代理逻辑
实现步骤
1. 添加依赖
首先在pom.xml
中添加必要的依赖:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> <version>4.5.14</version> </dependency>
2. 配置RestTemplate
@Configuration public class RestTemplateConfig { @Bean public RestTemplate restTemplate() { HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory(); factory.setConnectTimeout(5000); factory.setReadTimeout(5000); return new RestTemplate(factory); } }
3. 实现代理控制器
@RestController @RequestMapping("/proxy") public class ProxyController { @Autowired private RestTemplate restTemplate; // 目标服务器基础URL private static final String TARGET_SERVER = "http://target-service.com"; @RequestMapping("/**") public ResponseEntity<String> proxyRequest(HttpServletRequest request, @RequestBody(required = false) String body) { try { // 构建目标URL String requestUri = request.getRequestURI(); String proxyPath = requestUri.substring("/proxy".length()); String queryString = request.getQueryString(); StringBuilder targetUrl = new StringBuilder(TARGET_SERVER); targetUrl.append(proxyPath); if (queryString != null) { targetUrl.append("?").append(queryString); } // 复制请求头 HttpHeaders headers = new HttpHeaders(); Enumeration<String> headerNames = request.getHeaderNames(); while (headerNames.hasMoreElements()) { String headerName = headerNames.nextElement(); // 排除一些不需要转发的头部 if (!headerName.equalsIgnoreCase("host")) { headers.set(headerName, request.getHeader(headerName)); } } // 创建请求实体 HttpEntity<String> httpEntity = new HttpEntity<>(body, headers); // 确定HTTP方法 HttpMethod httpMethod = HttpMethod.valueOf(request.getMethod()); // 发送请求并获取响应 ResponseEntity<String> responseEntity = restTemplate.exchange(targetUrl.toString(), httpMethod, httpEntity, String.class); return responseEntity; } catch (Exception e) { return ResponseEntity .status(HttpStatus.INTERNAL_SERVER_ERROR) .body("代理请求失败: " + e.getMessage()); } } }
4. 处理文件上传和下载
对于需要处理文件上传和下载的情况,可以这样实现:
@PostMapping("/upload/**") public ResponseEntity<byte[]> proxyUpload(HttpServletRequest request, MultipartHttpServletRequest multipartRequest) { try { // 获取文件部分 MultiValueMap<String, Object> parts = new LinkedMultiValueMap<>(); multipartRequest.getFileMap().forEach((name, file) -> { try { ByteArrayResource resource = new ByteArrayResource(file.getBytes()) { @Override public String getFilename() { return file.getOriginalFilename(); } }; parts.add(name, resource); } catch (IOException e) { throw new RuntimeException(e); } }); // 获取其他表单参数 multipartRequest.getParameterMap().forEach((name, values) -> { for (String value : values) { parts.add(name, value); } }); // 构建URL String requestUri = request.getRequestURI(); String proxyPath = requestUri.substring("/proxy/upload".length()); String targetUrl = TARGET_SERVER + "/upload" + proxyPath; // 设置请求头 HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.MULTIPART_FORM_DATA); // 发送请求 HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(parts, headers); ResponseEntity<byte[]> response = restTemplate.exchange( targetUrl, HttpMethod.POST, requestEntity, byte[].class); return response; } catch (Exception e) { return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build(); } }
5. 增强功能:实现请求拦截和转换
通过添加拦截器,我们可以在请求前后进行处理:
@Configuration @Slf4j public class RestTemplateConfig { @Bean public RestTemplate restTemplate() { HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory(); factory.setConnectTimeout(5000); factory.setReadTimeout(5000); RestTemplate restTemplate = new RestTemplate(factory); // 添加拦截器 restTemplate.setInterceptors( Collections.singletonList((request, body, execution) -> { log.info("请求URL: {}", request.getURI()); // 请求前处理 HttpHeaders headers = request.getHeaders(); headers.add("X-Forwarded-By", "Spring-Proxy"); // 执行请求 ClientHttpResponse response = execution.execute(request, body); // 响应后处理 // 这里可以修改响应,例如添加头部、修改内容等 return response; }) ); return restTemplate; } }
6. 连接池配置
@Configuration @Slf4j public class RestTemplateConfig { @Bean public RestTemplate restTemplate() { /*HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory(); factory.setConnectTimeout(5000); factory.setReadTimeout(5000);*/ PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(); connectionManager.setMaxTotal(100); connectionManager.setDefaultMaxPerRoute(20); RequestConfig requestConfig = RequestConfig.custom() .setConnectionRequestTimeout(Timeout.of(5000, TimeUnit.MILLISECONDS)) .setResponseTimeout(Timeout.of(5000, TimeUnit.MILLISECONDS)) .build(); CloseableHttpClient httpClient = HttpClients.custom() .setConnectionManager(connectionManager) .setDefaultRequestConfig(requestConfig) .build(); HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory(httpClient); RestTemplate restTemplate = new RestTemplate(factory); // 添加拦截器 restTemplate.setInterceptors( Collections.singletonList((request, body, execution) -> { log.info("请求URL: {}", request.getURI()); // 请求前处理 HttpHeaders headers = request.getHeaders(); headers.add("X-Forwarded-By", "Spring-Proxy"); // 执行请求 ClientHttpResponse response = execution.execute(request, body); // 响应后处理 // 这里可以修改响应,例如添加头部、修改内容等 return response; }) ); return restTemplate; } }
安全考虑
请求验证:确保只转发合法请求 敏感信息过滤:过滤敏感头部或请求内容 限流措施:防止过多请求导致目标服务过载
// 示例:请求验证 private boolean validateRequest(HttpServletRequest request) { // 实现验证逻辑,例如检查认证信息、IP白名单等 String authToken = request.getHeader("Authorization"); return authToken != null && authService.isValidToken(authToken); }
总结
通过SpringBoot和RestTemplate,我们可以快速实现一个功能完备的反向代理。
相比于专门的代理服务器如Nginx,这种方式更加灵活,可以与业务逻辑紧密结合,适合实现特定的代理需求。
但对于大规模的代理场景,还是推荐使用专门的代理软件。
到此这篇关于SpringBoot利用RestTemplate实现反向代理的文章就介绍到这了,更多相关SpringBoot RestTemplate反向代理内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!