spring cloud Gateway简单使用

本文介绍了Spring Cloud Gateway的使用,包括基于Route和Hystrix实现微服务的默认降级策略,以及通过自定义过滤器进行登录态(token)校验。在实践中,注意Gateway与Spring Boot Web冲突需排除相关依赖,且Gateway Filter的错误码响应结构可能需要优化。
摘要由CSDN通过智能技术生成

一、引子

2年前有幸使用过一次Spring Cloud (1.5.9),那次用的是ZUUL做网关,没有使用Gateway做网关,一直是个小遗憾。终于在2年后的19年底再次使用Spring Cloud,这次有机会可以自己选择架构,果断使用Spring Cloud 全家桶。网关就是原生的Spring Cloud Gateway。项目架构图如下:

 

二、Gateway设计思想

2.1 官网设计

 

2.2 我们的使用

1.使用Route结合Hystrix实现默认降级策略

2.使用GatewayFilter实现登录态(token)校验

三、Gateway简单使用

3.1 实现微服务的默认降级策略

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

spring:

  cloud:

    gateway:

      discovery:

        locator:

          enabled: false

          #开启小写验证,默认feign根据服务名查找都是用的全大写

          lowerCaseServiceId: true

      default-filters:

        - AddResponseHeader=X-Response-Default-Foo, Default-Bar

      routes:

        - id: OLOAN-FINANCIAL-PRODUCT-SERVICE

          # lb代表从注册中心获取服务

          uri: lb://OLOAN-FINANCIAL-PRODUCT-SERVICE

          predicates:

            # 转发该路径

            - Path=/gateway/financialProduct/**

          # 带前缀

          filters:

            - StripPrefix=1

            - name: Hystrix

              args:

                name: fallbackcmd

                fallbackUri: forward:/defaultfallback

        - id: ADMIN-SERVICE

          uri: lb://ADMIN-SERVICE

          predicates:

            - Path=/gateway/auth/**

          filters:

            - StripPrefix=2

            - name: Hystrix

              args:

                name: fallbackcmd

                fallbackUri: forward:/defaultfallback

 如上图,我们开启了2个微服务route路由。

  • 1)前端请求时path带/gateway/,在gateway层使用StripPrefix=1,去掉gateway,最终微服务上的path不带"/gateway/".
  • 2)使用Hystrix实现默认降级策略,降级接口实现如下:

1

2

3

4

5

6

7

8

9

10

11

@Slf4j

@RestController

public class DefaultHystrixController {

 

    @RequestMapping("/defaultfallback")

    public ApiResult defaultfallback(){

 

        log.info("服务降级中");

        return ApiResult.failure("服务异常");

    }

}

 3.2 实现登录态(token)校验

3.2.1 自定义过滤器

自定义过滤器,实现GatewayFilter, Ordered 2个接口。

 1 import com.*.auth.UserTokenTools;
 2 import lombok.extern.slf4j.Slf4j;
 3 import org.apache.commons.lang3.StringUtils;
 4 import org.springframework.cloud.gateway.filter.GatewayFilter;
 5 import org.springframework.cloud.gateway.filter.GatewayFilterChain;
 6 import org.springframework.core.Ordered;
 7 import org.springframework.http.HttpHeaders;
 8 import org.springframework.http.HttpStatus;
 9 import org.springframework.http.server.reactive.ServerHttpRequest;
10 import org.springframework.http.server.reactive.ServerHttpResponse;
11 import org.springframework.stereotype.Component;
12 import org.springframework.web.server.ServerWebExchange;
13 import reactor.core.publisher.Mono;
14 
15 /**
16  * @author denny.zhang
17  * @Description token过滤器
18  * @date 2019/12/12 13:55
19  */
20 @Slf4j
21 @Component
22 public class LoginTokenFilter implements GatewayFilter, Ordered {
23 
24     private static final String AUTHORIZE_TOKEN = "Authorization";
25     private static final String BEARER = "Bearer ";
26 
27     /**
28      * token过滤
29      *
30      * @param exchange
31      * @param chain
32      * @return
33      */
34     @Override
35     public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
36         log.info("当前环境已开启token校验");
37         ServerHttpRequest request = exchange.getRequest();
38         HttpHeaders headers = request.getHeaders();
39         ServerHttpResponse response = exchange.getResponse();
40         // 取Authorization
41         String tokenHeader = headers.getFirst(AUTHORIZE_TOKEN);
42         log.info("tokenHeader=" + tokenHeader);
43         // token不存在
44         if (StringUtils.isEmpty(tokenHeader)) {
45             response.setStatusCode(HttpStatus.UNAUTHORIZED);
46             return response.setComplete();
47         }
48         // 取token
49         String token = this.getToken(tokenHeader);
50         log.info("token=" + token);
51 
52         // token不存在
53         if (StringUtils.isEmpty(token)) {
54             log.info("token不存在");
55             response.setStatusCode(HttpStatus.UNAUTHORIZED);
56             return response.setComplete();
57         }
58         // 校验 token是否失效
59         if (UserTokenTools.isTokenExpired(token, null)) {
60             log.info("token失效");
61             response.setStatusCode(HttpStatus.UNAUTHORIZED);
62             return response.setComplete();
63         }
64         // 校验 token是否正确
65         if (!UserTokenTools.checkToken(token, null)) {
66             response.setStatusCode(HttpStatus.UNAUTHORIZED);
67             return response.setComplete();
68         }
69 
70 //        //有token 这里可根据具体情况,看是否需要在gateway直接把解析出来的用户信息塞进请求中,我们最终没有使用
71 //        UserTokenInfo userTokenInfo = UserTokenTools.getUserTokenInfo(token);
72 //        log.info("token={},userTokenInfo={}",token,userTokenInfo);
73 //        request.getQueryParams().add("token",token);
74         //request.getHeaders().set("token", token);
75         return chain.filter(exchange);
76     }
77 
78 
79     @Override
80     public int getOrder() {
81         return -10;
82     }
83 
84     /**
85      * 解析Token
86      */
87     public String getToken(String requestHeader) {
88         //2.Cookie中没有从header中获取
89         if (requestHeader != null && requestHeader.startsWith(BEARER)) {
90             return requestHeader.substring(7);
91         }
92         return "";
93     }
94 }

上图中,UserTokenTools是我们自定义的一个JWT工具类,用来生成token,校验token过期、正确等。

3.2.2 配置路由

大家可根据具体情况,如果只有一套登录态,那就用一个filter即可。

 1 import com.*.gateway.filter.AuthorizeGatewayFilter;
 2 import com.*.gateway.filter.LoginTokenFilter;
 3 import org.springframework.cloud.gateway.route.RouteLocator;
 4 import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
 5 import org.springframework.context.annotation.Bean;
 6 import org.springframework.context.annotation.Configuration;
 7 
 8 
 9 @Configuration
10 public class GatewayConfig {
11 
12     @Bean
13     public RouteLocator getRouteLocator(RouteLocatorBuilder builder) {
14         return builder.routes()
15                 // token校验1
16                 .route(predicateSpec -> predicateSpec
17                         .path("/gateway/pay/card/**", "/gateway/app/**")
18                         .filters(gatewayFilterSpec -> gatewayFilterSpec.stripPrefix(1).filter(new AuthorizeGatewayFilter()))
19                         .uri("lb://OLOAN-PAY-SERVICE")
20                         .id("OLOAN-PAY-SERVICE-token"))
21 
22                 // token校验2
23                 .route(predicateSpec -> predicateSpec
24                         .path("/gateway/order-audit/**", "/gateway/order/**", "/gateway/order-payment/**")
25                         .filters(gatewayFilterSpec -> gatewayFilterSpec.stripPrefix(1).filter(new LoginTokenFilter()))
26                         .uri("lb://OLOAN-ORDER-SERVICE")
27                         .id("OLOAN-ORDER-ORDER-token"))
28                 .build();
29     }
30 }

四、总结

1.Spring Cloud Gateway使用WebFlux,和spring boot web包冲突,使用时一定记得pom中排除相关jar,否则会报错。

1

2

3

4

5

6

7

8

9

10

<dependency>

    <groupId>org.springframework.cloud</groupId>

    <artifactId>spring-cloud-starter-gateway</artifactId>

    <exclusions>

        <exclusion>

            <groupId>org.springframework</groupId>

            <artifactId>spring-webmvc</artifactId>

        </exclusion>

    </exclusions>

</dependency>

 

2.Gateway Filter 自带的源码支撑错误码response.setStatusCode(HttpStatus.UNAUTHORIZED);并不是那么的友好。错误码枚举使用的是spring自带框架的枚举类:

org.springframework.http.HttpStatus:

BAD_REQUEST(400, "Bad Request"),

 

。这样请求返回的结构体和一般定义的(code message data)不同。当然官方也是提供了解决方案。后续再去优化吧。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值