SpringCloud实战之路 | 应用篇(六)网关组件Gateway及其过滤器Filter

SpringCloud实战之路 | 应用篇(六)网关组件Gateway及其过滤器

文章内容输出来源:拉勾教育Java高薪训练营;

网关应用

在这里插入图片描述

Gateway工作流程

在这里插入图片描述
Spring Cloud Gateway是基于Reactor模型异步非阻塞的。

客户端向Spring Cloud Gateway发送请求。如果在Gateway Handler Mapping中找到与请求匹配的路由,则将其发送到Gateway Web Handler。Handler通过指定的过滤器链来将请求发送到我们实际的服务执行业务逻辑。然后返回。过滤器之间用虚线分库是因为过滤器可能会在发送代理请求前(pre)或后(post)执行业务逻辑。

  • Filter在pre时: 可以做参数校验、权限校验、限流、日志输出等
  • Filter在post时: 可以做响应内容、响应头的修改、日志输出、溜了监控等

代码实现

构建Gateway服务搭建

引入maven依赖

<?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">

    <modelVersion>4.0.0</modelVersion>

    <artifactId>cloud-gateway-server</artifactId>


    <!--spring boot 父启动器依赖-->
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.6.RELEASE</version>
    </parent>

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-commons</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <!--GateWay 网关-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
        </dependency>
        <!--引入webflux-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-webflux</artifactId>
        </dependency>
        <!--日志依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-logging</artifactId>
        </dependency>
        <!--测试依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <!--lombok工具-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.4</version>
            <scope>provided</scope>
        </dependency>

        <!--引入Jaxb,开始-->
        <dependency>
            <groupId>com.sun.xml.bind</groupId>
            <artifactId>jaxb-core</artifactId>
            <version>2.2.11</version>
        </dependency>
        <dependency>
            <groupId>javax.xml.bind</groupId>
            <artifactId>jaxb-api</artifactId>
        </dependency>
        <dependency>
            <groupId>com.sun.xml.bind</groupId>
            <artifactId>jaxb-impl</artifactId>
            <version>2.2.11</version>
        </dependency>
        <dependency>
            <groupId>org.glassfish.jaxb</groupId>
            <artifactId>jaxb-runtime</artifactId>
            <version>2.2.10-b140310.1920</version>
        </dependency>
        <dependency>
            <groupId>javax.activation</groupId>
            <artifactId>activation</artifactId>
            <version>1.1.1</version>
        </dependency>
        <!--引入Jaxb,结束-->

        <!-- Actuator可以帮助你监控和管理Spring Boot应用-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <!--热部署-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <optional>true</optional>
        </dependency>

        <!--Config 客户端依赖-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-config-client</artifactId>
        </dependency>

        <!--链路追踪-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-sleuth</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-zipkin</artifactId>
        </dependency>

    </dependencies>

    <dependencyManagement>
        <!--spring cloud依赖版本管理-->
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Greenwich.RELEASE</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <!--编译插件-->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>11</source>
                    <target>11</target>
                    <encoding>utf-8</encoding>
                </configuration>
            </plugin>
            <!--打包插件-->
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

创建入口启动类

package com.cloud;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

@SpringBootApplication
@EnableDiscoveryClient
public class GateWayApplication {

    public static void main(String[] args) {
        SpringApplication.run(GateWayApplication.class,args);
    }
}

创建配置文件yml

server:
  port: 9002
eureka:
  client:
    serviceUrl: # eureka server的路径
      defaultZone: http://LagouEurekaServerA:8761/eureka,http://LagouEurekaServerB:8762/eureka
  instance:
    prefer-ip-address: true
    instance-id: ${spring.cloud.client.ip-address}:${spring.application.name}:${server.port}:@project.version@
spring:
  application:
    name: cloud-gateway-server
  cloud:
    gateway:
      routes: # 路由可以有多个
        - id: service-code-router   
          uri: lb://cloud-service-code   
          predicates:             
            - Path=/api/code/**
          filters:
            - StripPrefix=1
        - id: service-email-router
          uri: lb://cloud-service-email
          predicates:
            - Path=/api/email/**
          filters:
            - StripPrefix=1
        - id: service-user-router       #自定义的路由id 要保证唯一
          uri: lb://cloud-service-user   #lb表示从注册中心获取的需要转发的服务名称
          #路由的规则 
          #例如访问localhost:9002/api/user/login,这里会路由到localhost:8080/user/login/
          predicates:            
            - Path=/api/user/**
          filters: # 可以去掉api之后转发
            - StripPrefix=1

GateWay过滤器

Spring Cloud Gateway过滤器分为两种GlobalFilter(应用到全部路由)和GateWayFilter(应用到单个路由)

GlobalFilter

基于自定义过滤器实现全局的黑白名单


@Slf4j
@Component // 让容器扫描到,等同于注册了
public class BlackListFilter implements GlobalFilter, Ordered {
    // 模拟⿊名单(实际可以去数据库或者redis中查询)
    private static List<String> blackList = new ArrayList<>();
    static {
        blackList.add("0:0:0:0:0:0:0:1"); // 模拟本机地址
    }
    /**
     * 过滤器核⼼⽅法
     * @param exchange 封装了request和response对象的上下⽂
     * @param chain ⽹关过滤器链(包含全局过滤器和单路由过滤器)
     * @return
     */
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        // 思路:获取客户端ip,判断是否在⿊名单中,在的话就拒绝访问,不在的话就放⾏
        // 从上下⽂中取出request和response对象
        ServerHttpRequest request = exchange.getRequest();
        ServerHttpResponse response = exchange.getResponse();
        // 从request对象中获取客户端ip
        String clientIp = request.getRemoteAddress().getHostString();
        // 拿着clientIp去⿊名单中查询,存在的话就决绝访问
        if(blackList.contains(clientIp)) {
            // 决绝访问,返回
            response.setStatusCode(HttpStatus.UNAUTHORIZED); // 状态码
            log.debug("=====>IP:" + clientIp + " 在⿊名单中,将被拒绝访问!");
            String data = "Request be denied!";
            DataBuffer wrap = response.bufferFactory().wrap(data.getBytes());
            return response.writeWith(Mono.just(wrap));
        }
        // 合法请求,放⾏,执⾏后续的过滤器
        return chain.filter(exchange);
    }
    /**
     * 返回值表示当前过滤器的顺序(优先级),数值越⼩,优先级越⾼
    */
    @Override
    public int getOrder() {
        return 0;
    }
}

GateWayFilter

针对某一个路由实现token信息验证

@Slf4j
public class TokenFilter implements GatewayFilter, Ordered {

    /**
     * 过滤器核心方法
     * @param exchange 封装了request和response对象的上下文
     * @param chain 网关过滤器链(包含全局过滤器和单路由过滤器)
     * @return
     */
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {

        ServerHttpRequest request = exchange.getRequest();
        ServerHttpResponse response = exchange.getResponse();

        List<HttpCookie> cookieList = request.getCookies().get("token");

        if (cookieList != null && cookieList.size() >0) {
            return chain.filter(exchange);
        }

        response.setStatusCode(HttpStatus.UNAUTHORIZED);

        String data = "token fail!!!";
        DataBuffer wrap = response.bufferFactory().wrap(data.getBytes());
        return response.writeWith(Mono.just(wrap));
    }


    /**
     * 返回值表示当前过滤器的顺序(优先级),数值越小,优先级越高
     * @return
     */
    @Override
    public int getOrder() {
        return 0;
    }
}
@Configuration
public class RouteLocatorConfig {

    @Bean
    public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
        return builder
                .routes()
                .route(r -> r.path("/api/user/info/**")
                        .filters(f -> f.stripPrefix(1).filter(new TokenFilter()))
                        .uri("lb://cloud-service-user")
                        .order(0)
                        .id("service-user-router"))
                .build();
    }
}

猜你喜欢

转载自blog.csdn.net/qq_23830637/article/details/106098117