future.png
珠玑随笔

联系:

future.png

版权 © 2022 by Hsu

技术: Gatsby

样式: Bulma

许可: CC BY NC SA 4.0

Spring Cloud 搭建 Gateway 网关

2022年01月14日

Hsu

1分钟

背景

Spring Cloud Gateway 是微服务架构中, API 的入口, 提供了路由、鉴权、流量控制、熔断、路径重写、⽇志监控等功能.

Spring Cloud Gateway 需要连接 Eurake 或其他服务发现组件读取路由表.

搭建

创建一个空项目, 引入以下依赖.

  <dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-gateway</artifactId>
  </dependency>

配置

Spring Cloud Gateway 提供三种配置的途径, 在生产环境中较多使用手动配置.

自动发现

在这种情况下 Gateway 自动发现所有的微服务.

spring.cloud.gateway.discovery.locator.enabled=true
spring.cloud.gateway.discovery.locator.lowerCaseServiceId=true

手动配置文件

我们较多使用手动配置, 可以写到 application 配置文件里.

spring:
  cloud:
    gateway:
      globalcors:
        cors-configurations:
          "[/**]":
            # 设置跨域地址
            allowedOrigins: "https://docs.spring.io"
            allowedMethods:
              - GET
              - POST
              - PUT
              - PATCH
              - DELETE
      routes:
        # id 属性用来区分, 没有实际作用
        - id: websocket_test
          # 地址
          uri: ws://localhost:9000
          # 匹配规则
          predicates:
            - Path=/echo
          # 过滤
          filters:
            # 加请求头
            - AddRequestHeader=X-Request-red, blue
        - id: users-ws
          uri: lb://users-ws
          predicates:
            - Path=/users/**
          filters:
            # 加响应头
            - AddResponseHeader=X-Response-Red, Blue
        - id: ingredients-fallback
          uri: http://localhost:9994
          predicates:
            - Path=/fallback
        - id: prefixpath_route
          uri: https://example.org
          filters:
            # 重定向
            - RedirectTo=302, https://www.baidu.com
        # SockJS route
        - id: websocket_sockjs_route
          uri: http://localhost:3001
          predicates:
            - Path=/websocket/info/**
        # Normal Websocket route
        - id: websocket_route
          uri: ws://localhost:3001
          predicates:
            - Path=/websocket/**

手动代码配置

这种方式可以直接在代码里编写, 只要注入 Bean 即可.

@Configuration
public class GateWayConfig {

  @Bean
  public RouteLocator customRouteLocator(RouteLocatorBuilder routeLocatorBuilder) {
    return routeLocatorBuilder.routes()
        // 导入负载均衡
        .route(p -> p.path("/exchange/**").uri("lb://exchange-service"))
        // 重定向到百度
        .route(p -> p.path("/baidu")
                     .filters(f -> f.rewritePath("/baidu(?<segment>.*)", "/${segment}"))
                     .uri("https://www.baidu.com"))
        // 添加头
        .route(p -> p.path("/news")
                     .filters(f -> f.addRequestHeader("Hello", "World"))
                     .uri("https://www.baidu.com"))
        .filters(f -> f.addRequestHeader("Hello", "World"))
        .build();
	}

}

Filter

我们可以自定义 Filter 用来过滤用户,比如 JWT 鉴权.

自定义 Filter 需要 WebFlux 技术.

以下为示例代码.

@Component
public class AuthorizationHeaderFilter extends
AbstractGatewayFilterFactory<AuthorizationHeaderFilter.Config> {

  // 注入环境变量
  @Autowired
  Environment env;

  // 或者使用 @Value
  @Value("${token.secret}")
  private String secret;

  public AuthorizationHeaderFilter() {
    super(Config.class);
  }

  public static class Config {
    // Put configuration properties here
  }

  @Override
  public GatewayFilter apply(Config config) {
    return (exchange, chain) -> {

      // 获取请求
      ServerHttpRequest request = exchange.getRequest();

      String authorizationHeader = request.getHeaders()
      .getFirst(HttpHeaders.AUTHORIZATION)

      // 获取Token
      String jwt = authorizationHeader.replace("Bearer", "");

      // 获取响应
      ServerHttpResponse response = exchange.getResponse();

      if (!isJwtValid(jwt)) {
        // 返回 401 错误
        response.setStatusCode(HttpStatus.UNAUTHORIZED);
        return response.setComplete();
      }

      // 放行
      return chain.filter(exchange);
    };
  }

  private Mono<Void> onError(
    ServerWebExchange exchange,
    String err,
    HttpStatus httpStatus) {
    ServerHttpResponse response = exchange.getResponse();
    response.setStatusCode(httpStatus);
    return response.setComplete();
	}

  private boolean isJwtValid(String jwt) {
    boolean returnValue = true;
    String secret env.getProperty("token.secret")
    // 校验逻辑
    return returnValue;
  }

}

把 AuthorizationHeaderFilter 添加到对应路由即可.

参考

https://spring.io/projects/spring-cloud-gateway

https://spring.io/guides/gs/gateway/

https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/

https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/appendix.html

https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/#writing-custom-gatewayfilter-factories