抱歉,您的浏览器无法访问本站
本页面需要浏览器支持(启用)JavaScript
了解详情 >

参考gateway使用
整合auth2:整合auth2
https://blog.csdn.net/weixin_43627706/article/details/124836962

gateway使用:

网关的作用

  • 作为API接口服务请求的接入点,管理所有的接入请求
  • 所有的业务服务都可以在这里被调用
  • 实现安全、验证、路由、过滤、流控,缓存等策略,进行一些必要的中介处理
  • 所有 API 统一管理

常见网关:

参考:网关的选择和使用

Nginx+Lua(OpenResty)、kong(基于OpenResty)、Zuul/Zuul2、Spring Cloud Gateway

(1)Kong 的性能非常不错,非常适合做流量网关,并且对于 service、route、upstream、consumer、plugins 的抽象,也是自研网关值得借鉴的。对于复杂系统,不建议业务网关用 Kong,或者更明确的说是不建议在 Java 技术栈的系统深度定制 Kong 或 OpenResty,主要是工程性方面的考虑。毕竟维护lua脚本的工作量和成本不低。

(2)pring Cloud Gateway/Zuul2 对于 Java 技术栈来说比较方便,可以依赖业务系统的一些 common jar。Lua 不方便,不光是语言的问题,更是复用基础设施的问题。另外,对于网关系统来说,性能不是差一个数量级,问题不大,多加 2 台机器就可以搞定。

(3)目前来看 Zuul2 的坑还是比较多的,因此作为java技术栈,比较建议使用 Spring Cloud Gateway 作为基础骨架。

介绍

Gateway是SpringCloud的一个全新项目,基于Spring5.0、Springboot2.0和Project Reactor等技术开发的网关,它旨在为微服务架构提供一种简单有效的统一的API路由管理方式。其作为SpringCloud生态系统中的网关,目标是替代Zuul,在SpringCloud2.0以上版本中,没有对新版本的Zuul2.0以上最新高性能版本进行集成,仍然还是使用的Zuul1.x非Reator模式的老版本。而为了提升网关的性能,Gateway是基于WebFlux框架实现的,二WebFlux框架底层则使用了高性能的Reactor模式通信框架Netty。

和zuul对比

  • Zuul 1.x 是阻塞式的高并发场景下效率低下
  • Gateway基于WebFlux是非阻塞式的异步框架
    • 引申:Spring WebFlux 是一个异步非阻塞式 IO 模型,通过少量的容器线程就可以支撑大量的并发访问。底层使用的是 Netty 容器,这点也和传统的 SpringMVC 不一样,SpringMVC 是基于 Servlet 的。
    • 使用参考:webflux使用

原理图:
image.png

gateway-client客户端
handler-mapping 根据断言匹配路由
web-handler 处理过滤器链
Filter在之前可以做参数校验、权限校验、流量监控、日志输出、协议转换等,在之后可以做响应内容&响应头的吸怪、日志的输出、流量监控等非常重要的作用。

核心概念

  • Route(路由):路由是构建网关的基本模块,它由ID,目标URI,一些列的断言和过滤器组成,如果断言为true则匹配改路由
  • Filter(过滤器):指的是GatewayFilter的实例,使用过滤器可以在请求被路由前或者后对请求进行修改
  • Filter(过滤器):指的是GatewayFilter的实例,使用过滤器可以在请求被路由前或者后对请求进行修改

Route主要配置项包括 :id、uri、 Predicates、path

  • id 路由的唯一标识,以服务名命名即可
  • uri 请求地址
  • predicates 主要起的作用是:基于Java8的Predicate,可以匹配Http请求中所有内容(例如请求头或者请求参数),如果请求与断言相匹配则进行路由
  • Path 配置对于请求路径的匹配规则,多个可用都好隔开、

++总结:条件predicates ,拦截filter,路由uri ++

路由:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
cloud:
nacos:
discovery:
server-addr: @env.cloud.nacos.server@
namespace: @env.cloud.nacos.namespace@
group: @env.cloud.nacos.group@
gateway:
discovery:
locator:
enabled: true
routes:
- id: steward
uri: lb://steward
predicates:
- Path=/auth/**,/user/**

- id: blog-sv
uri: lb://blog-sv
predicates:
- Path=/user/**

过滤器的使用

  • 全局过滤器: GlobalFilter
    基于全局过滤可以实现对客户端请求的处理,比如添加用户信息,以方便下游使用当前登陆的用户关键信息。
    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
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    @Component
    @Slf4j
    public class GlobalAuthenticationFilter implements GlobalFilter, Ordered {

    List<String> ignoreUrls= Arrays.asList();

    @Autowired
    private PemiFeignClient pemiFeignClient;

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {

    String requestUrl = exchange.getRequest().getPath().value();
    log.info("请求地址:{}",requestUrl);
    Date start=new Date();
    //1、白名单放行
    if (checkUrls(ignoreUrls,requestUrl)){
    return chain.filter(exchange);
    }
    try {
    //调用认证服务查询或者校验用户信息
    AccountLoginInfoParam accountLoginInfoParam=new AccountLoginInfoParam();
    accountLoginInfoParam.setLoginName("17686486497");
    BaseResponse<Account> response=pemiFeignClient.getAccountByLoginName(accountLoginInfoParam);
    if (!response.isSuccess() || response.getData()==null){
    return invalidTokenMono(exchange);
    }
    response.getData();
    String userInfo=JSON.toJSONString(response.getData());
    log.info("当前用户信息:{}",userInfo);
    //设置用户信息
    String base64 = Base64.encode(userInfo);
    ServerHttpRequest tokenRequest = exchange.getRequest().mutate().header(TokenConstant.TOKEN_NAME, base64).build();
    ServerWebExchange exchangeWithUserInfo = exchange.mutate().request(tokenRequest).build();
    Date end=new Date();
    log.info("请求耗时:{}ms",end.getTime()-start.getTime());
    return chain.filter(exchangeWithUserInfo);
    }catch (Exception e){
    e.printStackTrace();
    return invalidTokenMono(exchange);
    }
    }

    @Override
    public int getOrder() {
    return 0;
    }

    /**
    * 对url进行校验匹配
    */
    private boolean checkUrls(List<String> urls,String path){
    AntPathMatcher pathMatcher = new AntPathMatcher();
    for (String url : urls) {
    if (pathMatcher.match(url,path))
    return true;
    }
    return false;
    }

    /**
    * 从请求头中获取Token
    */
    private String getToken(ServerWebExchange exchange) {
    String tokenStr = exchange.getRequest().getHeaders().getFirst("Authorization");
    if (StringUtils.isBlank(tokenStr)) {
    return null;
    }
    String token = tokenStr.split(" ")[1];
    if (StringUtils.isBlank(token)) {
    return null;
    }
    return token;
    }

    /**
    * 无效的token
    */
    private Mono<Void> invalidTokenMono(ServerWebExchange exchange) {
    return buildReturnMono(RespFactory.fail(ErrCodeEnum.INVALID_TOKEN.code,ErrCodeEnum.INVALID_TOKEN.getMsg()), exchange);
    }


    private Mono<Void> buildReturnMono(BaseResponse baseResponse, ServerWebExchange exchange) {
    ServerHttpResponse response = exchange.getResponse();
    byte[] bits = JSON.toJSONString(baseResponse).getBytes(StandardCharsets.UTF_8);
    DataBuffer buffer = response.bufferFactory().wrap(bits);
    response.setStatusCode(HttpStatus.UNAUTHORIZED);
    response.getHeaders().add("Content-Type", "application/json;charset:utf-8");
    return response.writeWith(Mono.just(buffer));
    }
    }

遇到问题:

gateway中无法调用openfeign

Spring Cloud Gateway是基于WebFlux的,是ReactiveWeb,所以HttpMessageConverters不会自动注入,如HttpMessageConvertersAutoConfiguration源码所示。
image.png

解决方法

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
@SpringBootConfiguration
public class FeignConfig {

@Bean
@ConditionalOnMissingBean
public HttpMessageConverters messageConverters(ObjectProvider<HttpMessageConverter<?>> converters) {
return new HttpMessageConverters(converters.orderedStream().collect(Collectors.toList()));
}

@Bean
public Decoder feignDecoder() {
return new ResponseEntityDecoder(new SpringDecoder(feignHttpMessageConverter()));
}

public ObjectFactory<HttpMessageConverters> feignHttpMessageConverter() {
final HttpMessageConverters httpMessageConverters = new HttpMessageConverters(new GateWayMappingJackson2HttpMessageConverter());
return new ObjectFactory<HttpMessageConverters>() {
@Override
public HttpMessageConverters getObject() throws BeansException {
return httpMessageConverters;
}
};
}

public static class GateWayMappingJackson2HttpMessageConverter extends MappingJackson2HttpMessageConverter {
GateWayMappingJackson2HttpMessageConverter(){
List<MediaType> mediaTypes = new ArrayList<>();
mediaTypes.add(MediaType.valueOf(MediaType.TEXT_HTML_VALUE + "; charset=UTF-8"));
setSupportedMediaTypes(mediaTypes);
}
}

}

评论