背景:公司云环境外网访问不了sit环境,但是和外部公司对接的时候需要在sit环境联调,外网无法回调sit环境, 但是公司的生产环境可以请求sit环境
外网回调–》请求生产环境 —》 /test/** 路由转发到sit环境
TestProxyController.java
importlombok.extern.slf4j.Slf4j;importorg.apache.commons.io.FileUtils;importorg.springframework.beans.factory.annotation.Value;importorg.springframework.core.io.FileSystemResource;importorg.springframework.http.HttpEntity;importorg.springframework.http.HttpHeaders;importorg.springframework.http.HttpMethod;importorg.springframework.http.client.ClientHttpRequest;importorg.springframework.http.client.ClientHttpResponse;importorg.springframework.http.client.SimpleClientHttpRequestFactory;importorg.springframework.http.converter.StringHttpMessageConverter;importorg.springframework.util.LinkedMultiValueMap;importorg.springframework.util.MultiValueMap;importorg.springframework.util.StreamUtils;importorg.springframework.web.bind.annotation.RequestMapping;importorg.springframework.web.bind.annotation.RestController;importorg.springframework.web.client.RestTemplate;importorg.springframework.web.multipart.MultipartFile;importorg.springframework.web.multipart.MultipartHttpServletRequest;importjavax.servlet.http.HttpServletRequest;importjavax.servlet.http.HttpServletResponse;importjava.io.ByteArrayInputStream;importjava.io.File;importjava.io.IOException;importjava.io.InputStream;importjava.net.URI;importjava.net.URISyntaxException;importjava.nio.charset.StandardCharsets;importjava.util.ArrayList;importjava.util.Enumeration;importjava.util.List;importjava.util.Map;/**
* 参考:https://www.jianshu.com/p/450e12a148b1
* @date 2023/3/15
*/@Slf4j@RestControllerpublicclassTestProxyController{// 内网域名@Value("${test.url:https://x.sit.xxx.com}")privateString targetAddr;privatestaticRestTemplate restTemplate =newRestTemplate();publicstaticRestTemplategetRestemplate(){
restTemplate.getMessageConverters().set(1,newStringHttpMessageConverter(StandardCharsets.UTF_8));return restTemplate;}/**
* 代理所有请求
*
* @param request
* @param response
* @throws Exception
*/@RequestMapping(value ="/test/**")publicvoidproxy(HttpServletRequest request,HttpServletResponse response)throwsIOException,URISyntaxException{// String url = URLDecoder.decode(request.getRequestURL().toString(), "UTF-8");URI uri =newURI(request.getRequestURI());String path = uri.getPath();String query = request.getQueryString();String target = targetAddr + path.replace("/test","");if(query !=null&&!query.equals("")&&!query.equals("null")){
target = target +"?"+ query;}
log.info("testProxy target:{}", target);URI newUri =newURI(target);// 执行代理查询String methodName = request.getMethod();HttpMethod httpMethod =HttpMethod.resolve(methodName);if(httpMethod ==null){return;}InputStream stream =null;String contentType = request.getContentType();
log.info("testProxy target:{}, contentType:{}", target, contentType);// 兼容文件上传的请求if(contentType !=null&& contentType.startsWith("multipart/form-data")){MultipartHttpServletRequest mulReq =(MultipartHttpServletRequest) request;Map<String,MultipartFile> map = mulReq.getFileMap();List<MultipartFile> valueList =newArrayList<MultipartFile>(map.values());MultiValueMap<String,Object> params =newLinkedMultiValueMap();for(MultipartFile file : valueList){File newFile =File.createTempFile("temp", file.getOriginalFilename());FileUtils.copyInputStreamToFile(file.getInputStream(), newFile);FileSystemResource resource =newFileSystemResource(newFile);
params.add(file.getName(), resource);}Enumeration<String> headerNames = request.getHeaderNames();// 设置请求头HttpHeaders headers =newHttpHeaders();while(headerNames.hasMoreElements()){String headerName = headerNames.nextElement();
headers.set(headerName, request.getHeader(headerName));}// 手动设置请求头的token信息
headers.set("Authorization", request.getHeader("Authorization"));// 用HttpEntity封装整个请求报文HttpEntity<MultiValueMap<String,Object>> files =newHttpEntity<MultiValueMap<String,Object>>(params, headers);String res =getRestemplate().postForEntity(target, files,String.class).getBody();InputStream is =newByteArrayInputStream(res.getBytes("UTF-8"));
stream = is;}else{// 其他请求例如get post put delete都可使用ClientHttpRequest delegate =newSimpleClientHttpRequestFactory().createRequest(newUri, httpMethod);Enumeration<String> headerNames = request.getHeaderNames();// 设置请求头while(headerNames.hasMoreElements()){String headerName = headerNames.nextElement();Enumeration<String> v = request.getHeaders(headerName);List<String> arr =newArrayList<>();while(v.hasMoreElements()){
arr.add(v.nextElement());}
delegate.getHeaders().addAll(headerName, arr);}MyRequestWrapper wrapper =newMyRequestWrapper(request);StreamUtils.copy(wrapper.getInputStream(), delegate.getBody());
log.info("testProxy target:{}, proxyBody:{}", target, wrapper.getBody());// 执行远程调用ClientHttpResponse clientHttpResponse = delegate.execute();
response.setStatus(clientHttpResponse.getStatusCode().value());// 设置响应头
clientHttpResponse.getHeaders().forEach((key, value)-> value.forEach(it ->{// Spring boot框架会自己添加请求头,不过滤Transfer-Encoding和Content-Length可能会影响代理结果if(!key.equalsIgnoreCase("Transfer-Encoding")&&!key.equalsIgnoreCase("Content-Length")){
response.setHeader(key, it);}}));
stream = clientHttpResponse.getBody();}// 将获取到的输入流再次输出到页面输出流中StreamUtils.copy(stream, response.getOutputStream());}}
MyRequestWrapper.java,兼容x-www-form-urlencoded方式
importorg.apache.commons.io.IOUtils;importorg.apache.commons.lang3.StringUtils;importjavax.servlet.ReadListener;importjavax.servlet.ServletInputStream;importjavax.servlet.http.HttpServletRequest;importjavax.servlet.http.HttpServletRequestWrapper;importjava.io.BufferedReader;importjava.io.ByteArrayInputStream;importjava.io.IOException;importjava.io.InputStreamReader;importjava.net.URLEncoder;importjava.nio.charset.Charset;importjava.util.Arrays;importjava.util.Map;publicclassMyRequestWrapperextendsHttpServletRequestWrapper{privatefinalString body;publicMyRequestWrapper(HttpServletRequest request)throwsIOException{super(request);this.body =getBodyString(request);}publicStringgetBody(){return body;}publicstaticStringgetBodyString(finalHttpServletRequest request)throwsIOException{String contentType = request.getContentType();String bodyString ="";StringBuilder sb =newStringBuilder();if(StringUtils.isNotBlank(contentType)&&(contentType.contains("multipart/form-data")|| contentType.contains("x-www-form-urlencoded"))){Map<String,String[]> parameterMap = request.getParameterMap();for(Map.Entry<String,String[]> next : parameterMap.entrySet()){String[] values = next.getValue();String value =null;if(values !=null){if(values.length ==1){
value = values[0];}else{
value =Arrays.toString(values);}}
sb.append(next.getKey()).append("=").append(URLEncoder.encode(value,"utf8")).append("&");}if(sb.length()>0){
bodyString = sb.toString().substring(0, sb.toString().length()-1);}return bodyString;}else{returnIOUtils.toString(request.getInputStream(),Charset.forName("UTF-8"));}}@OverridepublicServletInputStreamgetInputStream()throwsIOException{finalByteArrayInputStream bais =newByteArrayInputStream(body.getBytes());returnnewServletInputStream(){@OverridepublicbooleanisFinished(){returnfalse;}@OverridepublicbooleanisReady(){returnfalse;}@Overridepublicintread(){return bais.read();}@OverridepublicvoidsetReadListener(ReadListener readListener){}};}@OverridepublicBufferedReadergetReader()throwsIOException{returnnewBufferedReader(newInputStreamReader(this.getInputStream()));}}
版权归原作者 项哥 所有, 如有侵权,请联系我们删除。