SpringCloud實戰之路 | 應用篇(六)網關組件Gateway及其過濾器Filter

SpringCloud實戰之路 | 應用篇(六)網關組件Gateway及其過濾器

網關應用

在這裏插入圖片描述

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();
    }
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章