详解SpringCloud微服务技术栈:Gateway网关(断言、过滤器、跨域问题)

发布时间:2024年01月17日

👨?🎓作者简介:一位大四、研0学生,正在努力准备大四暑假的实习
🌌上期文章:详解SpringCloud微服务技术栈:Feign远程调用、最佳实践、错误排查
📚订阅专栏:微服务技术全家桶
希望文章对你们有所帮助

现在集群中会有很多的微服务,且都对应自己的一个数据库,同时每个微服务还需要在Nacos中做注册和配置管理,当微服务之间有相互调用的时候,直接通过Feign来做远程调用,而当外部要访问的时候,直接发请求就可以了。

但,外部可以随意访问微服务,并不是很安全,有些东西只有相应的专业人员才能访问,而网关就是负责解决这类问题的,一切请求都要先通过了网关才能去访问微服务。

网关的作用介绍

身份认证和权限校验:通过才能去访问微服务
服务路由:当通过网关后,还需要根据请求的类型,将请求转发到对应的微服务中
负载均衡:确定了转发的微服务之后,微服务中的多个实例之间还应该做负载均衡
请求限流:限制访问的请求量

在SpringCloud中网关的实现包括2种:

gateway
zuul

zuul是基于Servlet的实现,属于阻塞式编程。而SpringCloudGateway则是基于Spring5中提供的webFlux,属于响应式编程的实现,具备更好的性能。
因此,我们会使用SpringCloudGateway。

网关快速入门

需要先搭建一下网关,步骤:
1、创新新的module,引入SpringCloudGateway的依赖和Nacos的服务注册发现依赖:

		<!--Nacos服务发现依赖-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
        <!--网关gateway依赖-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
        </dependency>

2、搭建一下启动类,位置cn.itcast.gateway.GatewayApplication:

@SpringBootApplication
public class GatewayApplication {
    public static void main(String[] args) {
        SpringApplication.run(GatewayApplication.class, args);
    }
}

2、编写网关的路由配置及Nacos地址:

server:
  port: 10010
spring:
  application:
    name: gateway
  cloud:
    nacos:
      server-addr: localhost:8848 # Nacos地址
    gateway:
      routes:
        - id: user-service # 路由的标识,必须唯一
          # uri: http://localhost:8081 # 固定的路由目标地址
          uri: lb://userservice # lb表示负载均衡,后面跟上服务的名称
          predicates: # 路由的断言,也就是判断请求能不能符合路由规则的条件
            - Path=/user/** # 路径满足/user/开头的就符合要求,托管给userservice服务
        - id: order-service
          uri: lb://orderservice
          predicates:
            - Path=/order/**

访问:
在这里插入图片描述
可以看到我们的网关本身并没有做什么操作,但是可以访问到相应信息,也就是说网关只是把请求转发给微服务。
流程图:
在这里插入图片描述

路由断言工厂

网关路由可以配置的信息包括:

路由id:路由唯一标识
uri:路由目的地,支持lb与http两种
predicates:路由断言,判断请求是否复杂要求,符合则转发到路由目的地
filters:路由过滤器,用于处理请求或者响应

我们在配置文件中写的断言规则只是字符串,这些字符串会被断言工厂(Predicate
Factory)读取并处理,转变为路由判断的条件。

Spring提供了11种基本的Predicate工厂:
在这里插入图片描述
如果不会写的话,就去Spring官网上面看实例,上面提供了11种断言工厂的演示。
在这里进行After的演示:

- After=2030-04-13T15:14:34.433+08:00[Asia/Shanghai]

只有在这个时间之后的请求才会符合这一条要求,显然这里是无法访问到的:
在这里插入图片描述
总结:

路由工厂的作用:读取用户的断言条件,对请求做出判断
Path=/user/**的作用:路径是以/user开头的就认为是符合的

路由的过滤器配置

GatewayFilter是网关中提供的一种过滤器,可以对进入网关的请求和微服务返回的响应做处理。
之前已经知道了,当用户发起请求的时候,要通过网关中的路由,基于断言工厂来确定将要去哪个微服务,但在这之间,网关可以给通过路由的请求再增加过滤器链,请求要经过这些过滤器链才能到达微服务,同样微服务返还数据给用户的时候也需要经过这些东西:
在这里插入图片描述
Spring提供了30多种不同的路由过滤工厂,这当然不是全部去学的,当有这方面的需求的时候就直接去看Spring官方文档就行了。
在这里进行一个简单的演示:给所有进入userservice的请求添加一个请求头。

实现方式:在gateway中修改application.yml文件,给userservice的路由添加过滤器
在这里插入图片描述
验证是否增加了这个请求头,只需要在UserController中获取请求头并且打印即可:

	@GetMapping("/{id}")
    public User queryById(@PathVariable("id") Long id, @RequestHeader(value = "Truth", required = false) String truth) {
        System.out.println("truth = " + truth);
        return userService.queryById(id);
    }

访问user网址后在控制台中可以看到输出:
在这里插入图片描述
而如果我们希望全局都可以增加这个过滤器,也就是全局都会增加这个请求头,只需要:
在这里插入图片描述
总结:

过滤器的作用:
(1)对路由的请求或响应做加工处理,比如添加请求头
(2)配置在路由下的过滤器只对当前路由的请求生效
defaultFilters的作用:对所有的路由都生效

全局过滤器GlobalFilter

全局过滤器的作用也是处理一切进入网关的请求和微服务响应,与GatewayFilter的作用一样。
区别在于GatewayFilter通过配置定义,处理逻辑是固定的。而GlobalFilter的逻辑需要自己写代码实现,而不是全部交给Spring容器,而且更灵活。
定义方式是实现GlobalFilter接口。
在这里插入图片描述
在这里定义定义一个全局过滤器,判断请求的参数是否满足:

1、参数中是否有authorization
2、authorization参数值是否为admin

如果是,则放行,否则拦截。
代码的编写还是比较麻烦的,因为这里面涉及了不少基于响应式编程的API,需要多花点时间敲才能更好的掌握。

//@Order(-1) //可以注解设置过滤器的优先级,越小则优先级越高,-1表示最高优先级
@Component //需要将实现设置为Spring的组件
public class AuthorizeFilter implements GlobalFilter, Ordered {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        //获取请求参数(通过上下文获取)
        ServerHttpRequest request = exchange.getRequest();
        MultiValueMap<String, String> params = request.getQueryParams();
        //获取参数中的authorization参数
        String auth = params.getFirst("authorization");
        //判断参数值是否等于admin
        if("admin".equals(auth)){
            //相等,放行
            //chain会直接找这个过滤器的下一个过滤器
            return chain.filter(exchange);
        }
        //不相等,拦截
        exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);//设置状态码,401表示未登录
        return exchange.getResponse().setComplete();
    }

    @Override
    public int getOrder() {//也可以用接口指定优先级
        return -1;
    }
}

在这里插入图片描述
总结:

全局过滤器的作用:对所有路由都生效,并且可以自定义处理逻辑
实现全局过滤器的步骤:
(1)实现GlobalFilter接口
(2)添加@Order接口或实现Ordered接口用于设定优先级
(3)编写处理逻辑

过滤器链执行顺序

请求进入网关会碰到三类过滤器:当前路由的过滤器、DefaultFilter、GlobalFilter。
请求路由后,会将当前路由过滤器和DefaultFilter、GlobalFilter合并到一个过滤器链中,排序后依次执行每个过滤器。

排序的规则:

每一个过滤器都必须指定一个int类型的order值,order值越小,优先级越高,执行顺序越靠前
(1)GlobalFilter通过实现Ordered接口,或者添加@Order注解来指定order值,由自己决定
(2)路由过滤器和defaultFilter的order是由Spring指定的,是按照配置文件中的声明顺序来递增的
当过滤器的order值一样时,会按照defaultFilter>路由过滤器>GlobalFilter的顺序执行。

记一下结论就行了,源码暂时还没去读。

网关的cors跨域配置

在web的时候已经解决过了跨域问题,而微服务里面也有这样的问题,因为所有的请求都要经过网关,也就是说,跨域请求不需要在微服务这里做处理,只需要在网关这里处理就行了。
而网关这里的实现不太一样,它没有Servlet相关的API,是响应式编程。

先重新分析一下跨域问题。

跨域,即域名不一致的就是跨域,主要包括:
(1)域名不同: www.taobao.com 和 www.taobao.org 和 www.jd.com 和 miaosha.jd.com
(2)域名相同,端口不同:localhost:8080和localhost8081
跨域问题:浏览器禁止请求的发起者与服务端发生跨域ajax请求,请求被浏览器拦截的问题
解决方案:CORS

在网关其实已经做好了CORS相关的底层逻辑了,只需要做相应的配置就可以解决问题了。

cloud:
    gateway:
      globalcors: # 全局跨域处理
        add-to-simple-url-handler-mapping: true # 解决options请求被拦截的问题
        corsConfiguration:
          '[/**]':
            allowedOrigins: # 允许哪些网站的跨域请求
              - "http://localhost:8090"
              - "http://www.leyou.com"
            allowedMethods: # 允许的跨域Ajax的请求方式
              - "GET"
              - "POST"
              - "DELETE"
              - "PUT"
              - "OPTIONS"
            allowedHeaders: "*" # 允许在请求中携带的头信息
            allowCredentials: true # 是否允许携带cookie
            maxAge: 360000 # 这次跨域检测的有效期
文章来源:https://blog.csdn.net/m0_52380556/article/details/135645187
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。