您的位置:首页 > 编程语言 > Java开发

spring cloud Gateway简单使用

2020-01-02 14:00 4631 查看

一、引子

2年前有幸使用过一次Spring Cloud (1.5.9),1.*集成的是ZUUL做网关,没有使用Gateway做网关,一直是个小遗憾。终于在2年后的19年底再次使用Spring Cloud,这次果断使用Spring Cloud 全家桶。网关就是原生的Spring Cloud Gateway(项目使用2.1.4)。一个简单的创业项目架构图如下:

二、Gateway设计思想

2.1 官网设计

自从撇开netflex zuul后,spring Cloud速度搜搜的。我开发时还是用2.1.4,目前最新已经到2.2.1,附上官网飞机票

2.1.1 特性

  • Built on Spring Framework 5, Project Reactor and Spring Boot 2.0:基于 Spring Framework 5,Project Reactor 和 Spring Boot 2.0 

  • Able to match routes on any request attribute.:能匹配任意请求属性的路由

  • Predicates and filters are specific to routes.:针对特定路由使用匹配策略和过滤器

  • Hystrix Circuit Breaker integration. :集成Hystri断路器

  • Spring Cloud DiscoveryClient integration:集成服务发现(gateway一样可注册到eureka)

  • Easy to write Predicates and Filters:易于写策略(断言)+过滤器

  • Request Rate Limiting 请求限流

  • Path Rewriting:重写path

简单来说就是Route、Predicate、Filter三大核心组件。

2.1.2 流程图

如上图,Gateway Client客户端发送请求在Gateway Handler Mapping中查找是否命中路由策略,命中的话请求转发给Gateway Web Handler来处理。根据定义的多个Filter链,执行顺序:Pre Filter->代理请求->Post Filter。

2.1.3 内置Predicates+Filter

Gateway内置了11个Predicates Factories路由策略(断言)工厂类。

Filter分2类:

  • 31个GatewayFilter Factories网关过滤器工厂类
  • 10个GlobalFilter 全局过滤器接口

这里就不在过多介绍,建议有需求时可以去官网找找,没有的话再自己开发。

2.2 我们的使用

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

2.使用GatewayFilter接口,自定义过滤器类,实现登录态(token)校验

三、Gateway简单使用

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

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个接口。

import com.*.auth.UserTokenTools;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.core.Ordered;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

/**
* @author denny.zhang
* @Description token过滤器
* @date 2019/12/12 13:55
*/
@Slf4j
@Component
public class LoginTokenFilter implements GatewayFilter, Ordered {

private static final String AUTHORIZE_TOKEN = "Authorization";
private static final String BEARER = "Bearer ";

/**
* token过滤
*
* @param exchange
* @param chain
* @return
*/
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
log.info("当前环境已开启token校验");
ServerHttpRequest request = exchange.getRequest();
HttpHeaders headers = request.getHeaders();
ServerHttpResponse response = exchange.getResponse();
// 取Authorization
String tokenHeader = headers.getFirst(AUTHORIZE_TOKEN);
log.info("tokenHeader=" + tokenHeader);
// token不存在
if (StringUtils.isEmpty(tokenHeader)) {
response.setStatusCode(HttpStatus.UNAUTHORIZED);
return response.setComplete();
}
// 取token
String token = this.getToken(tokenHeader);
log.info("token=" + token);

// token不存在
if (StringUtils.isEmpty(token)) {
log.info("token不存在");
response.setStatusCode(HttpStatus.UNAUTHORIZED);
return response.setComplete();
}
// 校验 token是否失效
if (UserTokenTools.isTokenExpired(token, null)) {
log.info("token失效");
response.setStatusCode(HttpStatus.UNAUTHORIZED);
return response.setComplete();
}
// 校验 token是否正确
if (!UserTokenTools.checkToken(token, null)) {
response.setStatusCode(HttpStatus.UNAUTHORIZED);
return response.setComplete();
}

//        //有token 这里可根据具体情况,看是否需要在gateway直接把解析出来的用户信息塞进请求中,我们最终没有使用
//        UserTokenInfo userTokenInfo = UserTokenTools.getUserTokenInfo(token);
//        log.info("token={},userTokenInfo={}",token,userTokenInfo);
//        request.getQueryParams().add("token",token);
//request.getHeaders().set("token", token);
return chain.filter(exchange);
}

@Override
public int getOrder() {
return -10;
}

/**
* 解析Token
*/
public String getToken(String requestHeader) {
//2.Cookie中没有从header中获取
if (requestHeader != null && requestHeader.startsWith(BEARER)) {
return requestHeader.substring(7);
}
return "";
}
}

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

3.2.2 配置路由

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

import com.*.gateway.filter.AuthorizeGatewayFilter;
import com.*.gateway.filter.LoginTokenFilter;
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class GatewayConfig {

@Bean
public RouteLocator getRouteLocator(RouteLocatorBuilder builder) {
return builder.routes()
// token校验1
.route(predicateSpec -> predicateSpec
.path("/gateway/pay/card/**", "/gateway/app/**")
.filters(gatewayFilterSpec -> gatewayFilterSpec.stripPrefix(1).filter(new AuthorizeGatewayFilter()))
.uri("lb://OLOAN-PAY-SERVICE")
.id("OLOAN-PAY-SERVICE-token"))

// token校验2
.route(predicateSpec -> predicateSpec
.path("/gateway/order-audit/**", "/gateway/order/**", "/gateway/order-payment/**")
.filters(gatewayFilterSpec -> gatewayFilterSpec.stripPrefix(1).filter(new LoginTokenFilter()))
.uri("lb://OLOAN-ORDER-SERVICE")
.id("OLOAN-ORDER-ORDER-token"))
.build();
}
}

四、总结

4.1.WebFlux

Spring Cloud Gateway使用WebFlux,和spring boot web包冲突,使用时一定记得pom中排除原来老WEB那一套(servlet)相关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>

 

4.2.Gateway Filter

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

org.springframework.http.HttpStatus:

BAD_REQUEST(400, "Bad Request"),

 

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

4.3 限流

gateway默认实现了几个简单的限流策略(依赖redis),后续可以使用一下。

 

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: