spring boot實現超輕量級閘道器(反向代理、轉發)
阿新 • • 發佈:2020-11-18
在我們的rest服務中,需要暴露一箇中間件的介面給使用者,但是需要經過rest服務的認證,這是典型的閘道器使用場景。可以引入閘道器元件來搞定,但是引入zuul等中介軟體會增加系統複雜性,這裡實現一個超輕量級的閘道器,只實現請求轉發,認證等由rest服務的spring security來搞定。
如何進行請求轉發呢? 熟悉網路請求的同學應該很清楚,請求無非就是請求方式、HTTP header,以及請求body,我們將這些資訊取出來,透傳給轉發的url即可。
舉例:
/graphdb/** 轉發到 Graph_Server/**
## 獲取轉發目的地址:
``` java
private String createRedictUrl(HttpServletRequest request, String routeUrl, String prefix) {
String queryString = request.getQueryString();
return routeUrl + request.getRequestURI().replace(prefix, "") +
(queryString != null ? "?" + queryString : "");
}
```
## 解析請求頭和內容
然後從request中提取出header、body等內容,構造一個`RequestEntity`,後續可以用`RestTemplate`來請求。
``` java
private RequestEntity createRequestEntity(HttpServletRequest request, String url) throws URISyntaxException, IOException {
String method = request.getMethod();
HttpMethod httpMethod = HttpMethod.resolve(method);
MultiValueMap headers = parseRequestHeader(request);
byte[] body = parseRequestBody(request);
return new RequestEntity<>(body, headers, httpMethod, new URI(url));
}
private byte[] parseRequestBody(HttpServletRequest request) throws IOException {
InputStream inputStream = request.getInputStream();
return StreamUtils.copyToByteArray(inputStream);
}
private MultiValueMap parseRequestHeader(HttpServletRequest request) {
HttpHeaders headers = new HttpHeaders();
List headerNames = Collections.list(request.getHeaderNames());
for (String headerName : headerNames) {
List headerValues = Collections.list(request.getHeaders(headerName));
for (String headerValue : headerValues) {
headers.add(headerName, headerValue);
}
}
return headers;
}
```
## 透明轉發
最後用`RestTemplate`來實現請求:
``` java
private ResponseEntity route(RequestEntity requestEntity) {
RestTemplate restTemplate = new RestTemplate();
return restTemplate.exchange(requestEntity, String.class);
}
```
## 全部程式碼
以下是輕量級轉發全部程式碼:
``` java
import org.springframework.http.*;
import org.springframework.stereotype.Service;
import org.springframework.util.MultiValueMap;
import org.springframework.util.StreamUtils;
import org.springframework.web.client.RestTemplate;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Collections;
import java.util.List;
@Service
public class RoutingDelegate {
public ResponseEntity redirect(HttpServletRequest request, HttpServletResponse response,String routeUrl, String prefix) {
try {
// build up the redirect URL
String redirectUrl = createRedictUrl(request,routeUrl, prefix);
RequestEntity requestEntity = createRequestEntity(request, redirectUrl);
return route(requestEntity);
} catch (Exception e) {
return new ResponseEntity("REDIRECT ERROR", HttpStatus.INTERNAL_SERVER_ERROR);
}
}
private String createRedictUrl(HttpServletRequest request, String routeUrl, String prefix) {
String queryString = request.getQueryString();
return routeUrl + request.getRequestURI().replace(prefix, "") +
(queryString != null ? "?" + queryString : "");
}
private RequestEntity createRequestEntity(HttpServletRequest request, String url) throws URISyntaxException, IOException {
String method = request.getMethod();
HttpMethod httpMethod = HttpMethod.resolve(method);
MultiValueMap headers = parseRequestHeader(request);
byte[] body = parseRequestBody(request);
return new RequestEntity<>(body, headers, httpMethod, new URI(url));
}
private ResponseEntity route(RequestEntity requestEntity) {
RestTemplate restTemplate = new RestTemplate();
return restTemplate.exchange(requestEntity, String.class);
}
private byte[] parseRequestBody(HttpServletRequest request) throws IOException {
InputStream inputStream = request.getInputStream();
return StreamUtils.copyToByteArray(inputStream);
}
private MultiValueMap parseRequestHeader(HttpServletRequest request) {
HttpHeaders headers = new HttpHeaders();
List headerNames = Collections.list(request.getHeaderNames());
for (String headerName : headerNames) {
List headerValues = Collections.list(request.getHeaders(headerName));
for (String headerValue : headerValues) {
headers.add(headerName, headerValue);
}
}
return headers;
}
}
```
## Spring 整合
Spring Controller,RequestMapping裡把GET \ POST\PUT\DELETE 支援的請求帶上,就能實現轉發了。
``` java
@RestController
@RequestMapping(GraphDBController.DELEGATE_PREFIX)
@Api(value = "GraphDB", tags = {
"graphdb-Api"
})
public class GraphDBController {
@Autowired
GraphProperties graphProperties;
public final static String DELEGATE_PREFIX = "/graphdb";
@Autowired
private RoutingDelegate routingDelegate;
@RequestMapping(value = "/**", method = {RequestMethod.GET, RequestMethod.POST, RequestMethod.PUT, RequestMethod.DELETE}, produces = MediaType.TEXT_PLAIN_VALUE)
public ResponseEntity catchAll(HttpServletRequest request, HttpServletResponse response) {
return routingDelegate.redirect(request, response, graphProperties.getGraphServer(), DELEGATE_PREFIX);
}
}
```
---
>作者:Jadepeng
出處:jqpeng的技術記事本--[http://www.cnblogs.com/xiaoqi](http://www.cnblogs.com/xiaoqi)
您的支援是對博主最大的鼓勵,感謝您的認真閱讀。
本文版權歸作者所有,歡迎轉載,但未經作者同意必須保留此段宣告,且在文章頁面明顯位置給出原文連線,否則保留追究法律責任的