spring boot实现超轻量级网关(反向代理、转发)

在我们的rest服务中,需要暴露一个中间件的接口给用户,但是需要经过rest服务的认证,这是典型的网关使用场景。可以引入网关组件来搞定,但是引入zuul等中间件会增加系统复杂性,这里实现一个超轻量级的网关,只实现请求转发,认证等由rest服务的spring security来搞定。

如何进行请求转发呢? 熟悉网络请求的同学应该很清楚,请求无非就是请求方式、HTTP header,以及请求body,我们将这些信息取出来,透传给转发的url即可。

举例:

/graphdb/** 转发到 Graph_Server/**

获取转发目的地址:#

  
   Copy
  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来请求。

  
   Copy
  private RequestEntity createRequestEntity(HttpServletRequest request, String url) throws URISyntaxException, IOException {
        String method = request.getMethod();
        HttpMethod httpMethod = HttpMethod.resolve(method);
        MultiValueMap<String, String> headers = parseRequestHeader(request);
        byte[] body = parseRequestBody(request);
        return new RequestEntity<>(body, headers, httpMethod, new URI(url));
    }
<span class="hljs-keyword">private</span> <span class="hljs-keyword">byte</span>[] parseRequestBody(HttpServletRequest request) <span class="hljs-keyword">throws</span> IOException {
    InputStream inputStream = request.getInputStream();
    <span class="hljs-keyword">return</span> StreamUtils.copyToByteArray(inputStream);
}

<span class="hljs-function"><span class="hljs-keyword">private</span> MultiValueMap&lt;String, String&gt; <span class="hljs-title">parseRequestHeader</span><span class="hljs-params">(HttpServletRequest request)</span> </span>{
    HttpHeaders headers = <span class="hljs-keyword">new</span> HttpHeaders();
    List&lt;String&gt; headerNames = Collections.list(request.getHeaderNames());
    <span class="hljs-keyword">for</span> (String headerName : headerNames) {
        List&lt;String&gt; headerValues = Collections.list(request.getHeaders(headerName));
        <span class="hljs-keyword">for</span> (String headerValue : headerValues) {
            headers.add(headerName, headerValue);
        }
    }
    <span class="hljs-keyword">return</span> headers;
}

透明转发#

最后用RestTemplate来实现请求:

 
  Copy
  private ResponseEntity<String> route(RequestEntity requestEntity) {
        RestTemplate restTemplate = new RestTemplate();
        return restTemplate.exchange(requestEntity, String.class);
    }

全部代码#

以下是轻量级转发全部代码:

 
  Copy
 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 {

<span class="hljs-function"><span class="hljs-keyword">public</span> ResponseEntity&lt;String&gt; <span class="hljs-title">redirect</span><span class="hljs-params">(HttpServletRequest request, HttpServletResponse response,String routeUrl, String prefix)</span> </span>{
    <span class="hljs-keyword">try</span> {
        <span class="hljs-comment">// build up the redirect URL</span>
        String redirectUrl = createRedictUrl(request,routeUrl, prefix);
        RequestEntity requestEntity = createRequestEntity(request, redirectUrl);
        <span class="hljs-keyword">return</span> route(requestEntity);
    } <span class="hljs-keyword">catch</span> (Exception e) {
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> ResponseEntity(<span class="hljs-string">"REDIRECT ERROR"</span>, HttpStatus.INTERNAL_SERVER_ERROR);
    }
}

<span class="hljs-function"><span class="hljs-keyword">private</span> String <span class="hljs-title">createRedictUrl</span><span class="hljs-params">(HttpServletRequest request, String routeUrl, String prefix)</span> </span>{
    String queryString = request.getQueryString();
    <span class="hljs-keyword">return</span> routeUrl + request.getRequestURI().replace(prefix, <span class="hljs-string">""</span>) +
            (queryString != <span class="hljs-keyword">null</span> ? <span class="hljs-string">"?"</span> + queryString : <span class="hljs-string">""</span>);
}


<span class="hljs-function"><span class="hljs-keyword">private</span> RequestEntity <span class="hljs-title">createRequestEntity</span><span class="hljs-params">(HttpServletRequest request, String url)</span> <span class="hljs-keyword">throws</span> URISyntaxException, IOException </span>{
    String method = request.getMethod();
    HttpMethod httpMethod = HttpMethod.resolve(method);
    MultiValueMap&lt;String, String&gt; headers = parseRequestHeader(request);
    <span class="hljs-keyword">byte</span>[] body = parseRequestBody(request);
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> RequestEntity&lt;&gt;(body, headers, httpMethod, <span class="hljs-keyword">new</span> URI(url));
}

<span class="hljs-function"><span class="hljs-keyword">private</span> ResponseEntity&lt;String&gt; <span class="hljs-title">route</span><span class="hljs-params">(RequestEntity requestEntity)</span> </span>{
    RestTemplate restTemplate = <span class="hljs-keyword">new</span> RestTemplate();
    <span class="hljs-keyword">return</span> restTemplate.exchange(requestEntity, String.class);
}


<span class="hljs-keyword">private</span> <span class="hljs-keyword">byte</span>[] parseRequestBody(HttpServletRequest request) <span class="hljs-keyword">throws</span> IOException {
    InputStream inputStream = request.getInputStream();
    <span class="hljs-keyword">return</span> StreamUtils.copyToByteArray(inputStream);
}

<span class="hljs-function"><span class="hljs-keyword">private</span> MultiValueMap&lt;String, String&gt; <span class="hljs-title">parseRequestHeader</span><span class="hljs-params">(HttpServletRequest request)</span> </span>{
    HttpHeaders headers = <span class="hljs-keyword">new</span> HttpHeaders();
    List&lt;String&gt; headerNames = Collections.list(request.getHeaderNames());
    <span class="hljs-keyword">for</span> (String headerName : headerNames) {
        List&lt;String&gt; headerValues = Collections.list(request.getHeaders(headerName));
        <span class="hljs-keyword">for</span> (String headerValue : headerValues) {
            headers.add(headerName, headerValue);
        }
    }
    <span class="hljs-keyword">return</span> headers;
}

}

Spring 集成#

Spring Controller,RequestMapping里把GET \ POST\PUT\DELETE 支持的请求带上,就能实现转发了。

 
  Copy
 @RestController
@RequestMapping(GraphDBController.DELEGATE_PREFIX)
@Api(value = "GraphDB", tags = {
        "graphdb-Api"
})
public class GraphDBController {
<span class="hljs-meta">@Autowired</span>
GraphProperties graphProperties;

<span class="hljs-keyword">public</span> <span class="hljs-keyword">final</span> <span class="hljs-keyword">static</span> String DELEGATE_PREFIX = <span class="hljs-string">"/graphdb"</span>;

<span class="hljs-meta">@Autowired</span>
<span class="hljs-keyword">private</span> RoutingDelegate routingDelegate;

<span class="hljs-meta">@RequestMapping(value = "/**", method = {RequestMethod.GET, RequestMethod.POST, RequestMethod.PUT, RequestMethod.DELETE}, produces = MediaType.TEXT_PLAIN_VALUE)</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> ResponseEntity <span class="hljs-title">catchAll</span><span class="hljs-params">(HttpServletRequest request, HttpServletResponse response)</span> </span>{
    <span class="hljs-keyword">return</span> routingDelegate.redirect(request, response, graphProperties.getGraphServer(), DELEGATE_PREFIX);
}

}

上一篇:Servlet


下一篇:javaweb-27:Filter实现权限拦截