[Cloud native & microservices > SCG gateway chapter 8] Spring Cloud Gateway tres formas de personalizar Filter y GlobalFilter

I. Introducción

Hasta ahora, se ha publicado la serie de artículos de puerta de enlace de microservicio:

  1. [Cloud Native & Microservices > SCG Gateway Part 1] Por qué hay una puerta de enlace y cómo elegir una puerta de enlace en un entorno de producción
  2. Cloud Native y Microservices > SCG Gateway Part 2] Esos métodos de liberación en escala de grises en producción
  3. [Cloud Native & Microservices > SCG Gateway Part 3] ¿Qué es Spring Cloud Gateway y casos de uso detallados?
  4. Cloud Native & Microservices > SCG Gateway Part 4] Cómo usar las 11 PredicateFactorys integradas en Spring Cloud Gateway
  5. [Cloud Native & Microservices > SCG Gateway Part 5] Spring Cloud Gateway Custom PredicateFactory
  6. [Cloud Native & Microservices > SCG Gateway Chapter 6] 18 posturas de uso de filtros integradas en Spring Cloud Gateway
  7. [Cloud Native & Microservices > SCG Gateway Chapter 6] Spring Cloud Gateway implementa la limitación, la fusión y el reintento actuales en función de los filtros integrados

Se trataron los siguientes temas:

  1. ¿Por qué tener una puerta de enlace? ¿Cuál es el papel de una puerta de enlace?
  2. ¿Clasificación de las puertas de enlace?
  3. ¿Selección de tecnología de puerta de enlace?
  4. ¿Cuáles son los métodos de publicación en escala de grises comúnmente utilizados cuando se utilizan puertas de enlace?
  5. ¿Qué es Spring Cloud Gateway? ¿Caso de uso detallado?
  6. 11 tipos de PredicateFactory integrados en Spring Cloud Gateway
  7. ¿Cómo personalizar PredicateFactory?
  8. 18 filtros de uso común integrados en Spring Cloud Gateway
  9. Spring Cloud Gateway implementa la limitación, la fusión y el reintento actuales en función de los filtros integrados

Luego, este artículo habla sobre cómo Spring Cloud Gateway personaliza Filter y GlobalFilter

PD: información de la versión de 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. Filtro personalizado

GatewayFilterFactory solo puede actuar en una sola ruta Ruta. Lo siguiente personaliza un filtro para implementar la función de registro;

1. Heredar de AbstractNameValueGatewayFilterFactory para implementar la función de registro

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 > Configurar Filtro para Ruta en 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> prueba:

Interfaz de acceso: http://127.0.0.1:9999/hello/sayHello, vea la salida de la consola del centro de la puerta de enlace de la siguiente manera:
inserte la descripción de la imagen aquí

2. Heredar de AbstractGatewayFilterFactory para implementar la función de registro

Cuando queremos especificar algunos otros parámetros para Filter, podemos hacerlo heredándolos de AbstractGatewayFilterFactory.

1> subclase 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 > Configurar Filtro para Ruta en 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> prueba:

Interfaz de acceso: http://127.0.0.1:9999/hello/sayHello, vea la salida de la consola del centro de la puerta de enlace de la siguiente manera:
inserte la descripción de la imagen aquí

3. Personalizar GlobalFilter

GlobalFilter actuará en todas las rutas de enrutamiento. El siguiente es un GlobalFilter personalizado para implementar la función de autenticación en el nivel de la puerta de enlace;

1. Personalice GlobalFilter para implementar la función de autenticación

1> GlobalFilter implementa la clase 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 no solo implementa GlobalFilter para filtrar todas las rutas, sino que también implementa la Orderedinterfaz y reescribe el método getORder() para devolver 0, lo que indica que el AuthFilter actual se ejecuta primero.

2> Introduzca commons-lang3 en pom.xml:

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

3> En el archivo application.yml, configure la URI sin autenticación:

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

4. Resumen

En este artículo, hablamos sobre tres formas de personalizar Filter/GlobalFilter, a saber: heredar AbstractNameValueGatewayFilterFactory, heredar AbstractGatewayFilterFactory e implementar la interfaz GlobalFilter. A continuación, hablemos de la integración de Spring Cloud Gateway y Nacos.

Supongo que te gusta

Origin blog.csdn.net/Saintmm/article/details/125829255
Recomendado
Clasificación