SpringCloud:搭建基于Gateway的微服务网关(二)

0.代码

https://github.com/fengdaizang/OpenAPI

1.引入相关依赖

pom文件如下:

复制代码
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>OpenAPI</artifactId>
        <groupId>com.fdzang.microservice</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
<span style="color: #0000ff;">&lt;</span><span style="color: #800000;">artifactId</span><span style="color: #0000ff;">&gt;</span>api-gateway<span style="color: #0000ff;">&lt;/</span><span style="color: #800000;">artifactId</span><span style="color: #0000ff;">&gt;</span>

<span style="color: #0000ff;">&lt;</span><span style="color: #800000;">dependencies</span><span style="color: #0000ff;">&gt;<br>     &lt;!-- 公共模块引入了web模块,会与gateway产生冲突,故排除 --&gt;</span>
    <span style="color: #0000ff;">&lt;</span><span style="color: #800000;">dependency</span><span style="color: #0000ff;">&gt;</span>
        <span style="color: #0000ff;">&lt;</span><span style="color: #800000;">groupId</span><span style="color: #0000ff;">&gt;</span>com.fdzang.microservice<span style="color: #0000ff;">&lt;/</span><span style="color: #800000;">groupId</span><span style="color: #0000ff;">&gt;</span>
        <span style="color: #0000ff;">&lt;</span><span style="color: #800000;">artifactId</span><span style="color: #0000ff;">&gt;</span>api-common<span style="color: #0000ff;">&lt;/</span><span style="color: #800000;">artifactId</span><span style="color: #0000ff;">&gt;</span>
        <span style="color: #0000ff;">&lt;</span><span style="color: #800000;">version</span><span style="color: #0000ff;">&gt;</span>1.0-SNAPSHOT<span style="color: #0000ff;">&lt;/</span><span style="color: #800000;">version</span><span style="color: #0000ff;">&gt;</span>
        <span style="color: #0000ff;">&lt;</span><span style="color: #800000;">exclusions</span><span style="color: #0000ff;">&gt;</span>
            <span style="color: #0000ff;">&lt;</span><span style="color: #800000;">exclusion</span><span style="color: #0000ff;">&gt;</span>
                <span style="color: #0000ff;">&lt;</span><span style="color: #800000;">groupId</span><span style="color: #0000ff;">&gt;</span>org.springframework.boot<span style="color: #0000ff;">&lt;/</span><span style="color: #800000;">groupId</span><span style="color: #0000ff;">&gt;</span>
                <span style="color: #0000ff;">&lt;</span><span style="color: #800000;">artifactId</span><span style="color: #0000ff;">&gt;</span>spring-boot-starter-web<span style="color: #0000ff;">&lt;/</span><span style="color: #800000;">artifactId</span><span style="color: #0000ff;">&gt;</span>
            <span style="color: #0000ff;">&lt;/</span><span style="color: #800000;">exclusion</span><span style="color: #0000ff;">&gt;</span>
        <span style="color: #0000ff;">&lt;/</span><span style="color: #800000;">exclusions</span><span style="color: #0000ff;">&gt;</span>
    <span style="color: #0000ff;">&lt;/</span><span style="color: #800000;">dependency</span><span style="color: #0000ff;">&gt;</span>

    <span style="color: #0000ff;">&lt;</span><span style="color: #800000;">dependency</span><span style="color: #0000ff;">&gt;</span>
        <span style="color: #0000ff;">&lt;</span><span style="color: #800000;">groupId</span><span style="color: #0000ff;">&gt;</span>org.projectlombok<span style="color: #0000ff;">&lt;/</span><span style="color: #800000;">groupId</span><span style="color: #0000ff;">&gt;</span>
        <span style="color: #0000ff;">&lt;</span><span style="color: #800000;">artifactId</span><span style="color: #0000ff;">&gt;</span>lombok<span style="color: #0000ff;">&lt;/</span><span style="color: #800000;">artifactId</span><span style="color: #0000ff;">&gt;</span>
    <span style="color: #0000ff;">&lt;/</span><span style="color: #800000;">dependency</span><span style="color: #0000ff;">&gt;</span>

    <span style="color: #0000ff;">&lt;</span><span style="color: #800000;">dependency</span><span style="color: #0000ff;">&gt;</span>
        <span style="color: #0000ff;">&lt;</span><span style="color: #800000;">groupId</span><span style="color: #0000ff;">&gt;</span>org.apache.commons<span style="color: #0000ff;">&lt;/</span><span style="color: #800000;">groupId</span><span style="color: #0000ff;">&gt;</span>
        <span style="color: #0000ff;">&lt;</span><span style="color: #800000;">artifactId</span><span style="color: #0000ff;">&gt;</span>commons-collections4<span style="color: #0000ff;">&lt;/</span><span style="color: #800000;">artifactId</span><span style="color: #0000ff;">&gt;</span>
    <span style="color: #0000ff;">&lt;/</span><span style="color: #800000;">dependency</span><span style="color: #0000ff;">&gt;</span>

    <span style="color: #0000ff;">&lt;</span><span style="color: #800000;">dependency</span><span style="color: #0000ff;">&gt;</span>
        <span style="color: #0000ff;">&lt;</span><span style="color: #800000;">groupId</span><span style="color: #0000ff;">&gt;</span>commons-io<span style="color: #0000ff;">&lt;/</span><span style="color: #800000;">groupId</span><span style="color: #0000ff;">&gt;</span>
        <span style="color: #0000ff;">&lt;</span><span style="color: #800000;">artifactId</span><span style="color: #0000ff;">&gt;</span>commons-io<span style="color: #0000ff;">&lt;/</span><span style="color: #800000;">artifactId</span><span style="color: #0000ff;">&gt;</span>
    <span style="color: #0000ff;">&lt;/</span><span style="color: #800000;">dependency</span><span style="color: #0000ff;">&gt;</span>

    <span style="color: #0000ff;">&lt;</span><span style="color: #800000;">dependency</span><span style="color: #0000ff;">&gt;</span>
        <span style="color: #0000ff;">&lt;</span><span style="color: #800000;">groupId</span><span style="color: #0000ff;">&gt;</span>commons-codec<span style="color: #0000ff;">&lt;/</span><span style="color: #800000;">groupId</span><span style="color: #0000ff;">&gt;</span>
        <span style="color: #0000ff;">&lt;</span><span style="color: #800000;">artifactId</span><span style="color: #0000ff;">&gt;</span>commons-codec<span style="color: #0000ff;">&lt;/</span><span style="color: #800000;">artifactId</span><span style="color: #0000ff;">&gt;</span>
    <span style="color: #0000ff;">&lt;/</span><span style="color: #800000;">dependency</span><span style="color: #0000ff;">&gt;</span>

    <span style="color: #0000ff;">&lt;</span><span style="color: #800000;">dependency</span><span style="color: #0000ff;">&gt;</span>
        <span style="color: #0000ff;">&lt;</span><span style="color: #800000;">groupId</span><span style="color: #0000ff;">&gt;</span>com.google.guava<span style="color: #0000ff;">&lt;/</span><span style="color: #800000;">groupId</span><span style="color: #0000ff;">&gt;</span>
        <span style="color: #0000ff;">&lt;</span><span style="color: #800000;">artifactId</span><span style="color: #0000ff;">&gt;</span>guava<span style="color: #0000ff;">&lt;/</span><span style="color: #800000;">artifactId</span><span style="color: #0000ff;">&gt;</span>
    <span style="color: #0000ff;">&lt;/</span><span style="color: #800000;">dependency</span><span style="color: #0000ff;">&gt;</span>

    <span style="color: #0000ff;">&lt;</span><span style="color: #800000;">dependency</span><span style="color: #0000ff;">&gt;</span>
        <span style="color: #0000ff;">&lt;</span><span style="color: #800000;">groupId</span><span style="color: #0000ff;">&gt;</span>com.google.code.gson<span style="color: #0000ff;">&lt;/</span><span style="color: #800000;">groupId</span><span style="color: #0000ff;">&gt;</span>
        <span style="color: #0000ff;">&lt;</span><span style="color: #800000;">artifactId</span><span style="color: #0000ff;">&gt;</span>gson<span style="color: #0000ff;">&lt;/</span><span style="color: #800000;">artifactId</span><span style="color: #0000ff;">&gt;</span>
    <span style="color: #0000ff;">&lt;/</span><span style="color: #800000;">dependency</span><span style="color: #0000ff;">&gt;</span>

    <span style="color: #0000ff;">&lt;</span><span style="color: #800000;">dependency</span><span style="color: #0000ff;">&gt;</span>
        <span style="color: #0000ff;">&lt;</span><span style="color: #800000;">groupId</span><span style="color: #0000ff;">&gt;</span>com.alibaba<span style="color: #0000ff;">&lt;/</span><span style="color: #800000;">groupId</span><span style="color: #0000ff;">&gt;</span>
        <span style="color: #0000ff;">&lt;</span><span style="color: #800000;">artifactId</span><span style="color: #0000ff;">&gt;</span>fastjson<span style="color: #0000ff;">&lt;/</span><span style="color: #800000;">artifactId</span><span style="color: #0000ff;">&gt;</span>
        <span style="color: #0000ff;">&lt;</span><span style="color: #800000;">version</span><span style="color: #0000ff;">&gt;</span>${fastjson.version}<span style="color: #0000ff;">&lt;/</span><span style="color: #800000;">version</span><span style="color: #0000ff;">&gt;</span>
    <span style="color: #0000ff;">&lt;/</span><span style="color: #800000;">dependency</span><span style="color: #0000ff;">&gt;<br></span>

     <!-- 引入gateway模块 -->    
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
<version>${spring.cloud.starter.version}</version>
</dependency>

     <!-- 引入eureka模块 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
<version>${spring.cloud.starter.version}</version>
</dependency>

     <!-- 引入openfeign模块,这里不要用feign,Springboot2.0已弃用 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
<version>${spring.cloud.starter.version}</version>
</dependency>

    <span style="color: #0000ff;">&lt;</span><span style="color: #800000;">dependency</span><span style="color: #0000ff;">&gt;</span>
        <span style="color: #0000ff;">&lt;</span><span style="color: #800000;">groupId</span><span style="color: #0000ff;">&gt;</span>org.springframework.cloud<span style="color: #0000ff;">&lt;/</span><span style="color: #800000;">groupId</span><span style="color: #0000ff;">&gt;</span>
        <span style="color: #0000ff;">&lt;</span><span style="color: #800000;">artifactId</span><span style="color: #0000ff;">&gt;</span>spring-cloud-starter-netflix-hystrix<span style="color: #0000ff;">&lt;/</span><span style="color: #800000;">artifactId</span><span style="color: #0000ff;">&gt;</span>
        <span style="color: #0000ff;">&lt;</span><span style="color: #800000;">version</span><span style="color: #0000ff;">&gt;</span>${spring.cloud.starter.version}<span style="color: #0000ff;">&lt;/</span><span style="color: #800000;">version</span><span style="color: #0000ff;">&gt;</span>
    <span style="color: #0000ff;">&lt;/</span><span style="color: #800000;">dependency</span><span style="color: #0000ff;">&gt;</span>

    <span style="color: #0000ff;">&lt;</span><span style="color: #800000;">dependency</span><span style="color: #0000ff;">&gt;</span>
        <span style="color: #0000ff;">&lt;</span><span style="color: #800000;">groupId</span><span style="color: #0000ff;">&gt;</span>org.springframework.boot<span style="color: #0000ff;">&lt;/</span><span style="color: #800000;">groupId</span><span style="color: #0000ff;">&gt;</span>
        <span style="color: #0000ff;">&lt;</span><span style="color: #800000;">artifactId</span><span style="color: #0000ff;">&gt;</span>spring-boot-configuration-processor<span style="color: #0000ff;">&lt;/</span><span style="color: #800000;">artifactId</span><span style="color: #0000ff;">&gt;</span>
        <span style="color: #0000ff;">&lt;</span><span style="color: #800000;">version</span><span style="color: #0000ff;">&gt;</span>${spring.boot.version}<span style="color: #0000ff;">&lt;/</span><span style="color: #800000;">version</span><span style="color: #0000ff;">&gt;</span>
        <span style="color: #0000ff;">&lt;</span><span style="color: #800000;">optional</span><span style="color: #0000ff;">&gt;</span>true<span style="color: #0000ff;">&lt;/</span><span style="color: #800000;">optional</span><span style="color: #0000ff;">&gt;</span>
    <span style="color: #0000ff;">&lt;/</span><span style="color: #800000;">dependency</span><span style="color: #0000ff;">&gt;</span>
<span style="color: #0000ff;">&lt;/</span><span style="color: #800000;">dependencies</span><span style="color: #0000ff;">&gt;</span>

</project>

复制代码

2.配置Gateway

复制代码
server:
  port: 7000

#注册到eureka eureka: client: service-url: defaultZone: http://127.0.0.1:7003/eureka/
#配置gateway拦截规则 spring: application: name: api-gateway cloud: gateway: discovery: locator: enabled: true routes: - id: gateway uri: http://www.baidu.com predicates: - Path=/**
#这里定义了鉴权的服务名,以及白名单 auth: service-id: api-auth-v1 gateway: white: - /login

这里是id生成器的配置,Twitter-Snowflake

IdWorker:
workerId: 122
datacenterId: 1231

复制代码

3.过滤器

3.1.ID生成拦截

对每个请求生成一个唯一的请求id

复制代码
package com.fdzang.microservice.gateway.gateway;

import com.fdzang.microservice.gateway.util.GatewayConstant;
import com.fdzang.microservice.gateway.util.IdWorker;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

/**

  • 生成一个请求的特定id

  • @author tanghu

  • @Date: 2019/11/5 18:42
    */
    @Slf4j
    @Component
    public class SerialNoFilter implements GlobalFilter, Ordered {

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
    ServerHttpRequest request
    = exchange.getRequest();

     String requestId</span>=<span style="color: #000000;"> request.getHeaders().getFirst(GatewayConstant.REQUEST_TRACE_ID);
     </span><span style="color: #0000ff;">if</span><span style="color: #000000;"> (StringUtils.isEmpty(requestId)) {
         Object attribute </span>=<span style="color: #000000;"> exchange.getAttribute(GatewayConstant.REQUEST_TRACE_ID);
         </span><span style="color: #0000ff;">if</span> (attribute == <span style="color: #0000ff;">null</span><span style="color: #000000;">) {
             requestId </span>=<span style="color: #000000;"> String.valueOf(IdWorker.getWorkerId());
             exchange.getAttributes().put(GatewayConstant.REQUEST_TRACE_ID,requestId);
         }
     }</span><span style="color: #0000ff;">else</span><span style="color: #000000;">{
         exchange.getAttributes().put(GatewayConstant.REQUEST_TRACE_ID,requestId);
     }
    
     </span><span style="color: #0000ff;">return</span><span style="color: #000000;"> chain.filter(exchange);
    

    }

    @Override
    public int getOrder() {
    return GatewayConstant.Order.SERIAL_NO_ORDER;
    }
    }

复制代码

3.2.鉴权拦截

获取请求头中的鉴权信息,对信息校验,这里暂时没有做(AuthResult authService.auth(AuthRequest request)),这里需求请求其他模块对请求信息进行校验,返回校验结果

复制代码
package com.fdzang.microservice.gateway.gateway;

import com.fdzang.microservice.common.entity.auth.AuthCode;
import com.fdzang.microservice.common.entity.auth.AuthRequest;
import com.fdzang.microservice.common.entity.auth.AuthResult;
import com.fdzang.microservice.gateway.service.AuthService;
import com.fdzang.microservice.gateway.util.GatewayConstant;
import com.fdzang.microservice.gateway.util.WhiteUrl;
import com.google.common.base.Joiner;
import com.google.common.base.Splitter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.math.NumberUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.HttpHeaders;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.util.MultiValueMap;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.util.List;
import java.util.Map;
import java.util.TreeMap;

/**

  • 权限校验

  • @author tanghu

  • @Date: 2019/10/22 18:00
    */
    @Slf4j
    @Component
    public class AuthFilter implements GlobalFilter, Ordered {

    @Autowired
    private AuthService authService;

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
    String requestId
    = exchange.getAttribute(GatewayConstant.REQUEST_TRACE_ID);
    String url
    = exchange.getRequest().getURI().getPath();

     ServerHttpRequest request </span>=<span style="color: #000000;"> exchange.getRequest();
    
     </span><span style="color: #008000;">//</span><span style="color: #008000;">跳过白名单</span>
     <span style="color: #0000ff;">if</span>(<span style="color: #0000ff;">null</span> != WhiteUrl.getWhite() &amp;&amp;<span style="color: #000000;"> WhiteUrl.getWhite().contains(url)){
         </span><span style="color: #0000ff;">return</span><span style="color: #000000;"> chain.filter(exchange);
     }
    
     </span><span style="color: #008000;">//</span><span style="color: #008000;">获取权限校验部分
     </span><span style="color: #008000;">//</span><span style="color: #008000;">Authorization: gateway:{AccessId}:{Signature}</span>
     String authHeader =<span style="color: #000000;"> exchange.getRequest().getHeaders().getFirst(GatewayConstant.AUTH_HEADER);
     </span><span style="color: #0000ff;">if</span><span style="color: #000000;">(StringUtils.isBlank(authHeader)){
         log.warn(</span>"request has no authorization header, uuid:{}, request:{}"<span style="color: #000000;">,requestId, url);
    
         </span><span style="color: #0000ff;">throw</span> <span style="color: #0000ff;">new</span> IllegalArgumentException("bad request"<span style="color: #000000;">);
     }
    
     List</span>&lt;String&gt; auths = Splitter.on(":"<span style="color: #000000;">).trimResults().omitEmptyStrings().splitToList(authHeader);
     </span><span style="color: #0000ff;">if</span>(CollectionUtils.isEmpty(auths) || auths.size() != 3 || !GatewayConstant.AUTH_LABLE.equals(auths.get(0<span style="color: #000000;">))){
         log.warn(</span>"bad authorization header, uuid:{}, request:[{}], header:{}"<span style="color: #000000;">,
                 requestId, url, authHeader);
    
         </span><span style="color: #0000ff;">throw</span> <span style="color: #0000ff;">new</span> IllegalArgumentException("bad request"<span style="color: #000000;">);
     }
    
     </span><span style="color: #008000;">//</span><span style="color: #008000;">校验时间戳是否合法</span>
     String timestamp =<span style="color: #000000;"> exchange.getRequest().getHeaders().getFirst(GatewayConstant.TIMESTAMP_HEADER);
     </span><span style="color: #0000ff;">if</span> (StringUtils.isBlank(timestamp) ||<span style="color: #000000;"> isTimestampExpired(timestamp)) {
         log.warn(</span>"wrong timestamp:{}, uuid:{}, request:{}"<span style="color: #000000;">,
                 timestamp, requestId, url);
     }
    
     String accessId </span>= auths.get(1<span style="color: #000000;">);
     String sign </span>= auths.get(2<span style="color: #000000;">);
    
     String stringToSign </span>=<span style="color: #000000;"> getStringToSign(request, timestamp);
    
     AuthRequest authRequest </span>= <span style="color: #0000ff;">new</span><span style="color: #000000;"> AuthRequest();
     authRequest.setAccessId(accessId);
     authRequest.setSign(sign);
     authRequest.setStringToSign(stringToSign);
     authRequest.setHttpMethod(request.getMethodValue());
     authRequest.setUri(url);
    
     AuthResult authResult </span>=<span style="color: #000000;"> authService.auth(authRequest);
    
     </span><span style="color: #0000ff;">if</span> (authResult.getStatus() !=<span style="color: #000000;"> AuthCode.SUCEESS.getAuthCode()) {
         log.warn(</span>"checkSign failed, uuid:{},  accessId:{}, request:[{}], error:{}"<span style="color: #000000;">,
                 requestId, accessId, url, authResult.getDescription());
         </span><span style="color: #0000ff;">throw</span> <span style="color: #0000ff;">new</span><span style="color: #000000;"> RuntimeException(authResult.getDescription());
     }
    
     log.info(</span>"request auth finished, uuid:{}, orgCode:{}, userName:{}, accessId:{}, request:{}, serviceName:{}"<span style="color: #000000;">,
             requestId, authResult.getOrgCode(),
             authResult.getUsername(), accessId,
             url, authResult.getServiceName());
    
     exchange.getAttributes().put(GatewayConstant.SERVICE_NAME,authResult.getServiceName());
    
     </span><span style="color: #0000ff;">return</span><span style="color: #000000;"> chain.filter(exchange);
    

    }

    /**

    • 获取原始字符串(签名前)

    • @param request

    • @param timestamp

    • @return
      */
      private String getStringToSign(ServerHttpRequest request, String timestamp){
      // headers
      TreeMap<String, String> headersInSign = new TreeMap<>();
      HttpHeaders headers
      = request.getHeaders();
      for (Map.Entry<String,List<String>> header:headers.entrySet()) {
      String key
      = header.getKey();
      if (key.startsWith(GatewayConstant.AUTH_HEADER_PREFIX)) {
      headersInSign.put(key, header.getValue().get(
      0));
      }
      }

      StringBuilder headerStringBuilder = new StringBuilder();
      for (Map.Entry<String, String> entry : headersInSign.entrySet()) {
      headerStringBuilder.append(entry.getKey()).append(
      "😊.append(entry.getValue()).append("\n");
      }
      String headerString
      = null;
      if (headerStringBuilder.length() != 0) {
      headerString
      = headerStringBuilder.deleteCharAt(headerStringBuilder.length()-1).toString();
      }

      // Url_String
      TreeMap<String, String> paramsInSign = new TreeMap<>();
      MultiValueMap
      <String, String> parameterMap = request.getQueryParams();
      if (MapUtils.isNotEmpty(parameterMap)) {
      for (Map.Entry<String, List<String>> entry : parameterMap.entrySet()) {
      paramsInSign.put(entry.getKey(), entry.getValue().get(
      0));
      }
      }

      // 原始url
      String originalUrl = request.getURI().getPath();

      StringBuilder uriStringBuilder = new StringBuilder(originalUrl);
      if (!parameterMap.isEmpty()) {
      uriStringBuilder.append(
      "?");
      for (Map.Entry<String, String> entry : paramsInSign.entrySet()) {
      uriStringBuilder.append(entry.getKey());
      if (StringUtils.isNotBlank(entry.getValue())) {
      uriStringBuilder.append(
      "=").append(entry.getValue());
      }
      uriStringBuilder.append(
      "&");
      }
      uriStringBuilder.deleteCharAt(uriStringBuilder.length()
      -1);
      }

      String uriString = uriStringBuilder.toString();

      String contentType = headers.getFirst(HttpHeaders.CONTENT_TYPE);

      //这里可以对请求参数进行MD5校验,暂时不做
      String contentMd5 = headers.getFirst(GatewayConstant.CONTENTE_MD5);

      String[] parts = {
      request.getMethodValue(),
      StringUtils.isNotBlank(contentMd5)
      ? contentMd5 : "",
      StringUtils.isNotBlank(contentType)
      ? contentType : "",
      timestamp,
      headerString,
      uriString
      };

      return Joiner.on(GatewayConstant.STRING_TO_SIGN_DELIM).skipNulls().join(parts);
      }

    /**

    • 校验时间戳是否超时

    • @param timestamp

    • @return
      */
      private boolean isTimestampExpired(String timestamp){
      long l = NumberUtils.toLong(timestamp, 0L);
      if (l == 0) {
      return true;
      }

      return Math.abs(System.currentTimeMillis() - l) > GatewayConstant.EXPIRE_TIME_SECONDS *1000;
      }

    @Override
    public int getOrder() {
    return GatewayConstant.Order.AUTH_ORDER;
    }
    }

复制代码

3.3.服务分发

根据鉴权后的结果能得到服务名,然后重写路由以及请求,对该次请求进行转发

复制代码
package com.fdzang.microservice.gateway.gateway;

import com.fdzang.microservice.gateway.util.GatewayConstant;
import com.fdzang.microservice.gateway.util.WhiteUrl;
import com.google.common.base.Splitter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.cloud.gateway.route.Route;
import org.springframework.cloud.gateway.support.ServerWebExchangeUtils;
import org.springframework.core.Ordered;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.util.List;

/**

  • @author tanghu

  • @Date: 2019/11/6 15:39
    */
    @Slf4j
    @Component
    public class ModifyRequestFilter implements GlobalFilter, Ordered {

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
    String url
    = exchange.getRequest().getURI().getPath();
    ServerHttpRequest request
    = exchange.getRequest();

     </span><span style="color: #008000;">//</span><span style="color: #008000;">跳过白名单</span>
     <span style="color: #0000ff;">if</span>(<span style="color: #0000ff;">null</span> != WhiteUrl.getWhite() &amp;&amp;<span style="color: #000000;"> WhiteUrl.getWhite().contains(url)){
         </span><span style="color: #0000ff;">return</span><span style="color: #000000;"> chain.filter(exchange);
     }
    
     String serviceName </span>=<span style="color: #000000;"> exchange.getAttribute(GatewayConstant.SERVICE_NAME);
    
     </span><span style="color: #008000;">//</span><span style="color: #008000;">修改路由</span>
     Route route =<span style="color: #000000;"> exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR);
     Route newRoute </span>=<span style="color: #000000;"> Route.async()
             .asyncPredicate(route.getPredicate())
             .filters(route.getFilters())
             .id(route.getId())
             .order(route.getOrder())
             .uri(GatewayConstant.URI.LOAD_BALANCE</span>+<span style="color: #000000;">serviceName).build();
    
     exchange.getAttributes().put(ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR,newRoute);
    
     </span><span style="color: #008000;">//</span><span style="color: #008000;">修改请求路径</span>
     List&lt;String&gt; strings = Splitter.on("/").omitEmptyStrings().trimResults().limit(3<span style="color: #000000;">).splitToList(url);
     String newServletPath </span>= "/" + strings.get(2<span style="color: #000000;">);
    
     ServerHttpRequest newRequest </span>=<span style="color: #000000;"> request.mutate().path(newServletPath).build();
    
     </span><span style="color: #0000ff;">return</span><span style="color: #000000;"> chain.filter(exchange.mutate().request(newRequest).build());
    

    }

    @Override
    public int getOrder() {
    return GatewayConstant.Order.MODIFY_REQUEST_ORDER;
    }
    }

复制代码

3.4.统一响应

对响应进行统一封装

复制代码
package com.fdzang.microservice.gateway.gateway;

import com.alibaba.fastjson.JSON;
import com.fdzang.microservice.common.entity.ApiResult;
import com.fdzang.microservice.gateway.entity.GatewayError;
import com.fdzang.microservice.gateway.entity.GatewayResult;
import com.fdzang.microservice.gateway.entity.GatewayResultEnums;
import com.fdzang.microservice.gateway.util.GatewayConstant;
import lombok.extern.slf4j.Slf4j;
import org.reactivestreams.Publisher;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.cloud.gateway.filter.NettyWriteResponseFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferFactory;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.http.server.reactive.ServerHttpResponseDecorator;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.nio.charset.Charset;

/**

  • @author tanghu

  • @Date: 2019/11/7 8:58
    */
    @Slf4j
    @Component
    public class ModifyResponseFilter implements GlobalFilter, Ordered {

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
    String requestId
    = exchange.getAttribute(GatewayConstant.REQUEST_TRACE_ID);
    ServerHttpResponse originalResponse
    = exchange.getResponse();
    DataBufferFactory bufferFactory
    = originalResponse.bufferFactory();
    ServerHttpResponseDecorator decoratedResponse
    = new ServerHttpResponseDecorator(originalResponse) {
    @Override
    public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {
    if (body instanceof Flux) {
    Flux
    <? extends DataBuffer> fluxBody = (Flux<? extends DataBuffer>) body;
    return super.writeWith(fluxBody.map(dataBuffer -> {
    byte[] content = new byte[dataBuffer.readableByteCount()];
    dataBuffer.read(content);
    //释放掉内存
    DataBufferUtils.release(dataBuffer);

                     String originalbody </span>= <span style="color: #0000ff;">new</span> String(content, Charset.forName("UTF-8"<span style="color: #000000;">));
                     String finalBody </span>=<span style="color: #000000;"> originalbody;
    
                     ApiResult apiResult </span>= JSON.parseObject(originalbody,ApiResult.<span style="color: #0000ff;">class</span><span style="color: #000000;">);
    
                     GatewayResult result </span>= <span style="color: #0000ff;">new</span><span style="color: #000000;"> GatewayResult();
                     result.setCode(GatewayResultEnums.SUCC.getCode());
                     result.setMsg(GatewayResultEnums.SUCC.getMsg());
                     result.setReq_id(requestId);
                     </span><span style="color: #0000ff;">if</span> (apiResult.getCode() == <span style="color: #0000ff;">null</span> &amp;&amp; apiResult.getMsg() == <span style="color: #0000ff;">null</span><span style="color: #000000;">) {
                         </span><span style="color: #008000;">//</span><span style="color: #008000;"> 尝试解析body为网关的错误信息</span>
                         GatewayError gatewayError = JSON.parseObject(originalbody,GatewayError.<span style="color: #0000ff;">class</span><span style="color: #000000;">);
                         result.setSub_code(gatewayError.getStatus());
                         result.setSub_msg(gatewayError.getMessage());
                     } </span><span style="color: #0000ff;">else</span><span style="color: #000000;"> {
                         result.setSub_code(apiResult.getCode());
                         result.setSub_msg(apiResult.getMsg());
                     }
    
                     result.setData(apiResult.getData());
    
                     finalBody </span>=<span style="color: #000000;"> JSON.toJSONString(result);
    
                     </span><span style="color: #0000ff;">return</span><span style="color: #000000;"> bufferFactory.wrap(finalBody.getBytes());
                 }));
             }
    
             </span><span style="color: #0000ff;">return</span> <span style="color: #0000ff;">super</span><span style="color: #000000;">.writeWith(body);
         }
     };
     </span><span style="color: #0000ff;">return</span><span style="color: #000000;"> chain.filter(exchange.mutate().response(decoratedResponse).build());
    

    }

    @Override
    public int getOrder() {
    return GatewayConstant.Order.MODIFY_RESPONSE_ORDER;
    }
    }

复制代码

4.测试

复制代码
10:25:54.961 [main] INFO  c.f.microservice.mock.util.SignUtil - StringToSign:
GET

1573093554201
/v2/base/zuul/tag/getMostUsedTags?from=2017-11-25 00:00:00&plate_num=部A11110&to=2017-11-30 00:00:00
10:25:54.979 [main] INFO c.f.microservice.mock.util.HttpUtil - sign:Y+usbpHlwOw4F2sq4b0pNjgXGDAXoYgs1syOOPxPFAE=
10:25:59.868 [main] INFO com.fdzang.microservice.mock.Demo - {"code":0,"data":[{"tagPublishedRefCount":3,"tagTitle":"Solo","id":"1533101769023","tagReferenceCount":3},{"tagPublishedRefCount":1,"tagTitle":"tetet","id":"1559285894006","tagReferenceCount":1}],"msg":"succ","req_id":"2627469547766022144","sub_code":0,"sub_msg":"ok"}

Process finished with exit code 0

复制代码

 由返回结果,可知此次请求完成。

5.注意事项

转发的目标服务需要跟网关注册在同一个注册中心下,路由uri配置为 lb://service_name,则会转发到对应的服务下,并且gateway会自动采用负载均衡机制

响应请求的顺序需要小于 NettyWriteResponseFilter.WRITE_RESPONSE_FILTER_ORDER 该值为-1

其他拦截器的顺序无固定要求,值越小越先执行

posted @ 2019-11-21 16:52  星朝  阅读(865)  评论(0编辑  收藏  举报