SpringCloud03

一、网关

网关:就是网络的关口,负责请求的路由,转发,身份校验。

在SpringCloud中网关的实现:

SpringCloud Gateway

由SpringCloud官方出品

基于WebFlux响应式编程

无需调优即可获得优异性能

(1)网关路由

配置路由规则

spring:
  cloud:
    gateway:
      routes: 
        - id: item # 路由规则id,自定义,唯一
          uri: lb://item-service # 路由目标微服务,lb代表负载均衡
          predicates: # 路由断言,判断请求是否符合规则,符合则路由到目标
            - Path=/items/** # 以请求路径做判断,以/items开头则符合
        - id: xx
          uri: lb://xx-service
          predicates:
            - Path=/xx/**

①快速入门:

创建一个新的模块,引入网关的依赖,编写启动类,配置路由规则。

②路由属性

网关路由对应的java类型是RouteDefinition,其中常见的属性有:

id:路由唯一标识

uri:路由目标地址

predicates:路由断言,判断请求是否符合当前路由

filters:路由过滤器,对请求或响应做特殊处理

路由断言: 

路由过滤器:

(2)网关登录校验

ctrl+H 查看该类的实现类

网关过滤器有两种,分别是:

GatewayFilter:路由过滤器作用于任意指定的路由,默认不生效,要配置到路由后生效。

GlobalFilter:全局过滤器,作用范围是所有路由,声明后自动生效。

①自定义全局过滤器

@Component
public class MyGlobalFilter implements GlobalFilter, Ordered {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
//        获取请求
        ServerHttpRequest request = exchange.getRequest();
        
        HttpHeaders headers = request.getHeaders();
        System.out.println("headers"+headers);
//        放行
        return chain.filter(exchange);
    }

    @Override
    public int getOrder() {
//        过滤器执行顺序,值越小,优先级越高
        return 0;
    }
}

②自定义过滤器GatewayFilter

自定义GatewayFilter不是直接实现GatewayFilter,而是实现AbstractGatewayFilterFactory


@Component
public class PrintAnyGatewayFilterFactory extends AbstractGatewayFilterFactory<Object> {

    @Override
    public GatewayFilter apply(Object config) {
        return new OrderedGatewayFilter(new GatewayFilter() {
            @Override
            public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
                System.out.println("print any filter running");
                return chain.filter(exchange);
            }
        },1);
    }
  }
}

③实现登录校验

@Component
@RequiredArgsConstructor
public class AuthGlobalFilter implements GlobalFilter, Ordered {
    private final AuthProperties authProperties;
    private final JwtTool jwtTool;
    private final AntPathMatcher antPathMatcher=new AntPathMatcher();
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
//        1.获取request
        ServerHttpRequest request = exchange.getRequest();
//        2.判断是否需要做登录拦截
        if (isExclude(request.getPath().toString())){
//            放行
            return chain.filter(exchange);
        }
//        3.获取token
        String token =null;
        List<String> headers = request.getHeaders().get("authorization");
        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.setStatusCode(HttpStatus.UNAUTHORIZED);
            return response.setComplete();
        }
//        TODO 5.传递用户信息
        System.out.println("userId"+userId);
//        6.放行
        return chain.filter(exchange);    }

    private boolean isExclude(String path) {
    for (String pathPattern :authProperties.getExcludePaths()){
        if (antPathMatcher.match(pathPattern,path)){
            return true;
        }
    }
    return false;
    }

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

④网关传递用户信息

一、在网关的登录校验过滤器中,把获取到的用户写入请求头

需求:修改gateway模块中的登录校验拦截器,在校验成功后保存用户到下游请求的请求头中。 提示:要修改转发到微服务的请求,需要用到ServerWebExchange类提供的API,示例如下:

//        5.传递用户信息
        String userInfo = userId.toString();
        ServerWebExchange swe = exchange.mutate().request(builder -> builder.header("userinfo", userInfo)).build();

从网关那里获取用户信息,通过过滤器做拦截,获取修改请求头,定义一个通用的拦截器,

二、在hm-common中编写SpringMVC拦截器,获取登录用户

需求:由于每个微服务都可能有获取登录用户的需求,因此我们直接在hm-common模块定义拦截器,这样微服务只需要引入依赖即可生效,无需重复编写。

@Component
public class UserInfoInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//      获取用户登陆信息
        String userinfo = request.getHeader("userinfo");
//      判断是否获取了用户,如果有,存入ThreadLocal
if (StrUtil.isNotBlank(userinfo)){
    UserContext.setUser(Long.valueOf(userinfo));

}
//        放行
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());
    }
}

spring.factories

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

⑤OpenFeign传递用户(微服务之间的相互调用)

微服务项目中的很多业务要多个微服务共同合作完成,而这个过程中也需要传递登录用户信息。

OpenFeign中提供了一个拦截器接口,所有由OpenFeign发起的请求都会先调用拦截器处理请求

    @Bean
    public RequestInterceptor userInfoRequestInterceptor(){
        return new RequestInterceptor() {
            @Override
            public void apply(RequestTemplate template) {
                Long userId = UserContext.getUser();
                if (userId!=null){
                RequestTemplate userinfo = template.header("userinfo", userId.toString());}
            }
        };
    }

(3)配置管理

①配置共享

i.添加一些通用配置到Nacos中,包括:Jdbc、MybatisPlus、日志、Swagger、OpenFeign等配置

ii.拉取共享配置

基于NacosConfig拉取共享配置代替微服务的本地配置

引入依赖

<!--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>

新建bootstrap.yaml

spring:
  application:
    name: cart-service #微服务名称
  profiles:
    active: dev
  cloud:
    nacos:
      server-addr: 1.12.232.19:8848 #nacos地址
      config:
        file-extension: yaml #文件后缀名
        shared-configs:
          - data-id: shared-jdbc.yaml
          - data-id: shared-log.yaml
          - data-id: shared-swagger.yaml

②配置热更新

配置热更新:当修改配置文件中的配置时,微服务无需重启即可使配置生效。

前提条件:nacos中要有一个与微服务名有关的配置文件。

微服务中要以特定方式读取需要热更新的配置属性

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

③动态路由

如果希望 Nacos 推送配置变更,可以使用 Nacos 动态监听配置接口来实现。

  • 创建ConfigService,目的是连接到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>

然后在网关gatewayresources目录创建bootstrap.yaml文件,内容如下

spring:
  application:
    name: gateway
  cloud:
    nacos:
      server-addr: 192.168.150.101
      config:
        file-extension: yaml
        shared-configs:
          - dataId: shared-log.yaml # 共享日志配置

接着,修改gatewayresources目录下的application.yml,把之前的路由移除

server:
  port: 8080
hm:
  jwt:
    location: classpath:hmall.jks
    alias: hmall
    password: hmall123
    tokenTTL: 30m
  auth:
    excludePaths:
      - /search/**
      - /users/login
      - /items/**
      - /hi

然后,在gateway中定义配置监听器

接下来,我们直接在Nacos控制台添加路由,路由文件名为gateway-routes.json,类型为json

[
    {
        "id": "item",
        "predicates": [{
            "name": "Path",
            "args": {"_genkey_0":"/items/**", "_genkey_1":"/search/**"}
        }],
        "filters": [],
        "uri": "lb://item-service"
    },
    {
        "id": "cart",
        "predicates": [{
            "name": "Path",
            "args": {"_genkey_0":"/carts/**"}
        }],
        "filters": [],
        "uri": "lb://cart-service"
    },
    {
        "id": "user",
        "predicates": [{
            "name": "Path",
            "args": {"_genkey_0":"/users/**", "_genkey_1":"/addresses/**"}
        }],
        "filters": [],
        "uri": "lb://user-service"
    },
    {
        "id": "trade",
        "predicates": [{
            "name": "Path",
            "args": {"_genkey_0":"/orders/**"}
        }],
        "filters": [],
        "uri": "lb://trade-service"
    },
    {
        "id": "pay",
        "predicates": [{
            "name": "Path",
            "args": {"_genkey_0":"/pay-orders/**"}
        }],
        "filters": [],
        "uri": "lb://pay-service"
    }
]

  • 19
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值