spring cloud Gateway simple to use

First, the primer

2 years ago I have been used once Spring Cloud (1.5.9), that use the gateway ZUUL do, do not use the Gateway Gateway has been a little regret. Finally use Spring Cloud again at the end of 192 years later, this time have the opportunity to choose their own infrastructure, determined using Spring Cloud family bucket. Gateway is a native of Spring Cloud Gateway. Project structure is as follows:

Two, Gateway design

2.1 official website design

 

2.2 Our use

1. Route combine Hystrix implement default policy downgrade

2. GatewayFilter implement login state (token) check

Three, Gateway is simple to use

3.1 Downgrade default strategy to achieve micro services

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实现默认降级策略,降级接口实现如下:
@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,否则会报错。

<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)不同。当然官方也是提供了解决方案。后续再去优化吧。

 

Guess you like

Origin www.cnblogs.com/dennyzhangdd/p/12132612.html