[クラウドネイティブとマイクロサービス>SCGゲートウェイ第8章]SpringCloudGatewayフィルターとGlobalFilterをカスタマイズする3つの方法

I.はじめに

これまでに、マイクロサービスゲートウェイシリーズの記事が公開されています。

  1. [クラウドネイティブとマイクロサービス>SCGゲートウェイパート1]ゲートウェイがある理由と本番環境でゲートウェイを選択する方法
  2. クラウドネイティブとマイクロサービス>SCGゲートウェイパート2]本番環境でのこれらのグレースケールリリース方法
  3. [クラウドネイティブとマイクロサービス>SCGゲートウェイパート3]SpringCloudGatewayとは何ですか?詳細なユースケース
  4. クラウドネイティブとマイクロサービス>SCGゲートウェイパート4]SpringCloudGatewayで11の組み込みPredicateFactoryを使用する方法
  5. [クラウドネイティブとマイクロサービス>SCGゲートウェイパート5]SpringCloudGatewayカスタムPredicateFactory
  6. [クラウドネイティブとマイクロサービス>SCGゲートウェイ第6章]SpringCloudGatewayに組み込まれた18のフィルター使用姿勢
  7. [クラウドネイティブとマイクロサービス>SCGゲートウェイ第6章]SpringCloud Gatewayは、組み込みのフィルターに基づいて、現在の制限、融合、および再試行を実装します

次の問題について話し合いました。

  1. なぜゲートウェイがあるのですか?ゲートウェイの役割は何ですか?
  2. ゲートウェイの分類?
  3. ゲートウェイテクノロジーの選択?
  4. ゲートウェイを使用するときに一般的に使用されるグレースケール公開方法は何ですか?
  5. Spring Cloud Gatewayとは何ですか?詳細なユースケース?
  6. SpringCloudGatewayに組み込まれた11種類のPredicateFactory
  7. PredicateFactoryをカスタマイズする方法は?
  8. SpringCloudGatewayに組み込まれている18の一般的に使用されるフィルター
  9. Spring Cloud Gatewayは、組み込みのフィルターに基づいて、現在の制限、融合、および再試行を実装します

次に、この記事では、SpringCloudGatewayがFilterとGlobalFilterをカスタマイズする方法について説明します。

PS:SpringCloudのバージョン情報:

<properties>
    <spring-boot.version>2.4.2</spring-boot.version>
    <spring-cloud.version>2020.0.1</spring-cloud.version>
    <spring-cloud-alibaba.version>2021.1</spring-cloud-alibaba.version>
</properties>

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-dependencies</artifactId>
            <version>${spring-boot.version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
        <!--整合spring cloud-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>${spring-cloud.version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
        <!--整合spring cloud alibaba-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-alibaba-dependencies</artifactId>
            <version>${spring-cloud-alibaba.version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

2.カスタムフィルター

GatewayFilterFactoryは、単一のルートルートでのみ機能できます。以下は、ロギングの機能を実装するためにフィルターをカスタマイズします。

1. AbstractNameValueGatewayFilterFactoryから継承して、ロギング関数を実装します

1> AbstractNameValueGatewayFilterFactory子クラスMyLogNameValueGatewayFilterFactory:

package com.saint.gateway.filter;

import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractNameValueGatewayFilterFactory;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Mono;

/**
 * 统一日志记录Filter
 * 继承自AbstractNameValueGatewayFilterFactory,内置了两个参数:name和value;
 *
 * @author Saint
 */
@Component
@Slf4j
public class MyLogNameValueGatewayFilterFactory extends AbstractNameValueGatewayFilterFactory {
    
    
    @Override
    public GatewayFilter apply(NameValueConfig config) {
    
    
        return ((exchange, chain) -> {
    
    
            // Filter的前置处理Prev
            log.info("MyLogGatewayFilterFactory [Pre] Filter Request, config.getName() = " + config.getName());
            return chain.filter(exchange).then(Mono.fromRunnable(() -> {
    
    
                // 在then方法中是请求执行结束之后的后置处理,即Filter的POST
                log.info("MyLogGatewayFilterFactory [Post] Response Filter");
            }));
        });
    }
}

2> application.ymlでルートのフィルターを構成します:

server:
  port: 9999
spring:
  cloud:
    gateway:
      routes:
        - id: add_request_parameter_route
          uri: http://127.0.0.1:9001
          predicates:
            - Path=/**
          filters:
            # 自定义过滤器的名字,即:MyLogNameValueGatewayFilterFactory
            - name: MyLogNameValue
              args:
                name: Saint_name
                value: Saint_value

3>テスト:

アクセスインターフェイス:http://127.0.0.1:9999/hello/sayHello、次のようにゲートウェイセンターコンソールの出力を参照してください。
ここに画像の説明を挿入

2. AbstractGatewayFilterFactoryから継承して、ロギング機能を実装します

Filterに他のパラメーターを指定する場合は、AbstractGatewayFilterFactoryから継承することで指定できます。

1> AbstractGatewayFilterFactoryサブクラスMyLogGatewayFilterFactory:

package com.saint.gateway.filter;

import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Mono;

/**
 * 统一日志记录Filter
 * AbstractGatewayFilterFactory,可以自定义参数;
 *
 * @author Saint
 */
@Component
@Slf4j
public class MyLogGatewayFilterFactory extends AbstractGatewayFilterFactory<MyLogGatewayFilterFactory.MyConfig> {
    
    

    public MyLogGatewayFilterFactory() {
    
    
        super(MyConfig.class);
    }

    @Override
    public GatewayFilter apply(MyConfig config) {
    
    
        return ((exchange, chain) -> {
    
    
            // Filter的前置处理Prev
            log.info("MyLogGatewayFilterFactory [Pre] Filter Request, config.getName() = " + config.getName());
            return chain.filter(exchange).then(Mono.fromRunnable(() -> {
    
    
                // 在then方法中是请求执行结束之后的后置处理,即Filter的POST
                log.info("MyLogGatewayFilterFactory [Post] Response Filter");
            }));
        });
    }


    /**
     * MyConfig只是一个配置类,该类中只有一个属性name。这个属性可以在yml文件中使用
     */
    public static class MyConfig {
    
    
        private String name;

        public String getName() {
    
    
            return name;
        }

        public void setName(String name) {
    
    
            this.name = name;
        }
    }
}

2> application.ymlでルートのフィルターを構成します:

server:
  port: 9999
spring:
  cloud:
    gateway:
      routes:
        - id: add_request_parameter_route
          uri: http://127.0.0.1:9001
          predicates:
            - Path=/**
          filters:
            # 自定义过滤器的名字,即:MyLogGatewayFilterFactory
            - name: MyLog
              args:
                name: Saint_log_name

3>テスト:

アクセスインターフェイス:http://127.0.0.1:9999/hello/sayHello、次のようにゲートウェイセンターコンソールの出力を参照してください。
ここに画像の説明を挿入

3.GlobalFilterをカスタマイズします

GlobalFilterは、すべてのルーティングルートに作用します。以下は、ゲートウェイレベルで認証機能を実装するためのカスタムGlobalFilterです。

1. GlobalFilterをカスタマイズして、認証機能を実装します

1> GlobalFilterはクラスAuthFilterを実装します:

package com.saint.gateway.filter;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.HttpCookie;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.util.List;

/**
 * 对所有路由生效的GlobalFilter,鉴权使用
 *
 * @author Saint
 */
@Component
public class AuthFilter implements GlobalFilter, Ordered {
    
    
    private static final Logger logger = LoggerFactory.getLogger(AuthFilter.class);

    @Value("#{'${auth.skip.urls:}'.split(',')}")
    private List<String> skipAuthUrls;

    /**
     * 存放token信息的请求头属性
     */
    public static final String COOKIE_NAME_TOKEN = "saint_token";

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
    
    
        //1.获取请求对象
        ServerHttpRequest request = exchange.getRequest();
        //2.获取响应对象
        ServerHttpResponse response = exchange.getResponse();

        //3.判断 是否需要直接放行
        logger.info("uri: {}", request.getURI());
        if (skipAuthUrls.contains(request.getURI().toString())) {
    
    
            return chain.filter(exchange);
        }

        //4 校验
        //4.1 从头header中获取令牌数据
        String token = request.getHeaders().getFirst(COOKIE_NAME_TOKEN);

        if (StringUtils.isEmpty(token)) {
    
    
            //4.2 从cookie中中获取令牌数据
            HttpCookie first = request.getCookies().getFirst(COOKIE_NAME_TOKEN);
            if (first != null) {
    
    
                token = first.getValue();//就是令牌的数据
            }
        }

        if (StringUtils.isEmpty(token)) {
    
    
            //4.3 从请求参数中获取令牌数据
            token = request.getQueryParams().getFirst(COOKIE_NAME_TOKEN);
        }

        if (StringUtils.isEmpty(token)) {
    
    
            //4.4. 如果没有数据    没有登录,要重定向到登录到页面
            response.setStatusCode(HttpStatus.FORBIDDEN);//403
            // 4.5. 在响应头里添加鉴权失败信息
            response.getHeaders().set("auth_error", "xxxx");
            return response.setComplete();
        }


        //TODO 5 解析令牌数据 ( 判断解析是否正确,正确 就放行 ,否则 结束)

        try {
    
    

            logger.info("authed user info: {}", "*****");
        } catch (Exception e) {
    
    
            logger.error("parse token error:", e);
            //解析失败
            response.setStatusCode(HttpStatus.FORBIDDEN);
            return response.setComplete();
        }

        // 6 将token添加到头信息,传递给后续的服务
        request.mutate().header(COOKIE_NAME_TOKEN, token);
        return chain.filter(exchange);
    }

    /**
     * 这里表示将当前Filter放在过滤链的最前面
     *
     * @return
     */
    @Override
    public int getOrder() {
    
    
        return 0;
    }
}

AuthFilterは、GlobalFilterを実装してすべてのルートをフィルタリングするだけでなく、Orderedインターフェイスも実装し、getORder()メソッドを書き換えて0を返し、現在のAuthFilterが最初に実行されることを示します。

2> commons-lang3をpom.xmlに導入します:

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-lang3</artifactId>
    <version>3.12.0</version>
</dependency>

3> application.ymlファイルで、認証なしでURIを設定します。

auth:
  skip:
    urls: http://localhost:9001/service-a/hello, http://localhost:9001/service-b/hello

4.まとめ

この記事では、Filter / GlobalFilterをカスタマイズする3つの方法、つまり、AbstractNameValueGatewayFilterFactoryの継承、AbstractGatewayFilterFactoryの継承、およびGlobalFilterインターフェースの実装について説明しました。次に、SpringCloudGatewayとNacosの統合について説明しましょう。

おすすめ

転載: blog.csdn.net/Saintmm/article/details/125829255