0


【SpringCloud】Gateway

一、网关路由

1.1.认识网关

网关就是络的口。数据在网络间传输,从一个网络传输到另一网络时就需要经过网关来做数据的路由****和转发以及数据安全的校验。前端请求不能直接访问微服务,而是要请求网关:

  • 网关可以做安全控制,也就是登录身份校验,校验通过才放行
  • 通过认证后,网关再根据请求判断应该访问哪个微服务,将请求转发过去

1.2.快速入门

1.2.1.引入依赖

        <!--网关-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
        </dependency>
        <!--nacos discovery-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
        <!--负载均衡-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-loadbalancer</artifactId>
        </dependency>

1.2.2.配置路由

server:
  port: 8080
spring:
  application:
    name: gateway
  cloud:
    nacos:
      server-addr: 192.168.150.101:8848
    gateway:
      routes:
        - id: item-service # 路由规则id,自定义,唯一
          uri: lb://item-service # 路由的目标服务,lb代表负载均衡,会从注册中心拉取服务列表
          predicates: # 路由断言,判断当前请求是否符合当前规则,符合则路由到目标服务
            - Path=/items/**,/search/** # 这里是以请求路径作为判断规则
        - id: cart-service
          uri: lb://cart-service
          predicates:
            - Path=/carts/**

id:建议和微服务名保持一致

uri:路由目标

lb:负载均衡 ://服务名称

predicates:路由断言

  • Path:以路径做匹配

二、网关登录校验

2.1.Gateway工作原理

  1. 客户端请求进入网关后由HandlerMapping对请求做判断,找到与当前请求匹配的路由规则(**Route**),然后将请求交给WebHandler去处理。
  2. WebHandler则会加载当前路由下需要执行的过滤器链(Filter chain),然后按照顺序逐一执行过滤器(后面称为Filter)。
  3. 图中Filter被虚线分为左右两部分,是因为Filter内部的逻辑分为prepost两部分,分别会在请求路由到微服务之前和之后被执行。
  4. 只有所有Filterpre逻辑都依次顺序执行通过后,请求才会被路由到微服务。
  5. 微服务返回结果后,再倒序执行Filterpost逻辑。
  6. 最终把响应结果返回。

2.2.自定义过滤器

网关过滤器链中的过滤器有两种:

  • **GatewayFilter**:路由过滤器,作用范围比较灵活,可以是任意指定的路由Route.
  • **GlobalFilter**:全局过滤器,作用范围是所有路由,不可配置。

2.3.登录校验

自定义GlobalFilter完成登录校验

自定义登录校验过滤器

 public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        //1、获取请求头
        ServerHttpRequest request = exchange.getRequest();
        //2.判断是否不需要拦截
        if (isExclude(request.getPath().toString())) {
            return chain.filter(exchange);
        }
        //3.获取token
        List<String> headers = request.getHeaders().get("authorization");
        String token = null;
        if (headers != null && !headers.isEmpty()) {
            token = headers.get(0);
        }
        //4.解析校验token
        Long userId = null;
        try {
            userId = jwtTool.parseToken(token);
        } catch (UnauthorizedException e) {
            //拦截,设置响应状态码
            ServerHttpResponse response = exchange.getResponse();
            response.setRawStatusCode(401);
            return response.setComplete();
        }
        //System.out.println("userId = " + userId);
        //保存用户到请求头
        String userInfo = userId.toString();
        ServerWebExchange swe = exchange.mutate().//mutate 对下游请求做更改
                request(builder -> builder.header("user-info", userInfo))
                .build();

        //6.放行
        return chain.filter(swe);
    }

2.4.微服务获取用户

2.4.1.保存用户信息到请求头

        //保存用户到请求头
        String userInfo = userId.toString();
        ServerWebExchange swe = exchange.mutate().//mutate 对下游请求做更改
                request(builder -> builder.header("user-info", userInfo))
                .build();

2.4.2.拦截器获取用户

/**
 * 拦截器,获取用户保存到ThreadLocal
 */
public class UserInfoInterceptor implements HandlerInterceptor {
    //在controller执行之前运行
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        //1.获取用户信息
        String userInfo = request.getHeader("user-info");
        //2.将用户信息保存到ThreadLocal
        if (StrUtil.isNotBlank(userInfo)) {
            UserContext.setUser(Long.valueOf(userInfo));
        }
        //3.放行,只获取用户信息,不做拦截
        return true;
    }

    //清理用户
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        UserContext.removeUser();
    }
}

配置拦截器

@Configuration
@ConditionalOnClass(DispatcherServlet.class)
public class MvcConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new UserInfoInterceptor());
    }
}

不过,需要注意的是,这个配置类默认是不会生效的,因为它所在的包是

com.hmall.common.config

,与其它微服务的扫描包不一致,无法被扫描到,因此无法生效。

基于SpringBoot的自动装配原理,我们要将其添加到

resources

目录下的

META-INF/spring.factories

文件中:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
  com.hmall.common.config.MyBatisConfig,\
  com.hmall.common.config.MvcConfig

2.5.OpenFeign传递用户

利用

RequestTemplate

类来添加请求头,将用户信息保存到请求头中。这样以来,每次OpenFeign发起请求的时候都会调用该方法,传递用户信息。

    @Bean
    public RequestInterceptor UserInfoRequestInterceptor() {
        return requestTemplate -> {
            Long userId = UserContext.getUser();
            if (userId != null) {
                requestTemplate.header("user-info", userId.toString());
            }
        };
    }

三、配置管理

3.1.配置共享

我们可以把微服务共享的配置抽取到Nacos中统一管理,这样就不需要每个微服务都重复配置了。分为两步:

  • 在Nacos中添加共享配置
  • 微服务拉取配置

3.2.拉取配置共享

3.2.1.引入依赖

  <!--nacos配置管理-->
  <dependency>
      <groupId>com.alibaba.cloud</groupId>
      <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
  </dependency>
  <!--读取bootstrap文件-->
  <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-starter-bootstrap</artifactId>
  </dependency>

3.2.2.创建bootstrap.yaml

spring:
  application:
    name: cart-service # 服务名称
  profiles:
    active: dev
  cloud:
    nacos:
      server-addr: 192.168.150.101 # nacos地址
      config:
        file-extension: yaml # 文件后缀名
        shared-configs: # 共享配置
          - dataId: shared-jdbc.yaml # 共享mybatis配置
          - dataId: shared-log.yaml # 共享日志配置
          - dataId: shared-swagger.yaml # 共享日志配置

3.3.配置热更新

3.3.1.添加配置到Nacos

dataId格式:

[服务名]-[spring.active.profile].[后缀名]

文件名称由三部分组成:

  • **服务名**:购物车服务,所以是cart-service
  • **spring.active.profile**:就是spring boot中的spring.active.profile,可以省略,则所有profile共享该配置
  • **后缀名**:例如yaml

3.3.2.配置热更新

创建属性读取类

@Data
@Component
@ConfigurationProperties(prefix = "hm.cart")
public class CartProperties {
    private Integer maxAmount;
}

3.4.动态路由

四、总结


本文转载自: https://blog.csdn.net/Etenal_/article/details/142025794
版权归原作者 柒末雪丶温存 所有, 如有侵权,请联系我们删除。

“【SpringCloud】Gateway”的评论:

还没有评论