当前位置: 首页 > news >正文

SpringCloudGateway网关技术

一、认识网关

在这里插入图片描述在这里插入图片描述

二、网关服务初始化

1、新建网关module、引入依赖

<dependencies><!--common,自己的通用工具类--><dependency><groupId>com.heima</groupId><artifactId>hm-common</artifactId><version>1.0.0</version></dependency><!--网关--><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></dependencies>

2、配置路由

server:port: 8080
spring:application:name: gatewaycloud:nacos:server-addr: 192.168.150.101:8848,若是使用window,可以配本地的地址gateway:routes:- id: item # 路由规则id,自定义,唯一uri: lb://item-service # 路由的目标服务,lb代表负载均衡,会从注册中心拉取服务列表predicates: # 路由断言,判断当前请求是否符合当前规则,符合则路由到目标服务- Path=/items/**,/search/** # 这里是以请求路径作为判断规则- id: carturi: lb://cart-servicepredicates:- Path=/carts/**- id: useruri: lb://user-servicepredicates:- Path=/users/**,/addresses/**- id: tradeuri: lb://trade-servicepredicates:- Path=/orders/**- id: payuri: lb://pay-servicepredicates:- Path=/pay-orders/**//以上完成后,即可完成路由转发

网关鉴权(登录校验)

1、网关请求处理流程

在这里插入图片描述在这里插入图片描述
如图所示:

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

如图中所示,最终请求转发是有一个名为NettyRoutingFilter的过滤器来执行的,而且这个过滤器是整个过滤器链中顺序最靠后的一个。如果我们能够定义一个过滤器,在其中实现登录校验逻辑,并且将过滤器执行顺序定义到NettyRoutingFilter之前,这就符合我们的需求了

2、自定义过滤器

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

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

这里以GlobalFilter为例

/*** 处理请求并将其传递给下一个过滤器* @param exchange 当前请求的上下文,其中包含request、response等各种数据* @param chain 过滤器链,基于它向下传递请求* @return 根据返回值标记当前请求是否被完成或拦截,chain.filter(exchange)就放行了。*/
Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain);

自定义GlobalFilter

@Component
public class PrintAnyGlobalFilter implements GlobalFilter, Ordered {@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {// 编写过滤器逻辑System.out.println("未登录,无法访问");// 放行// return chain.filter(exchange);// 拦截ServerHttpResponse response = exchange.getResponse();response.setRawStatusCode(401);return response.setComplete();}@Overridepublic int getOrder() {// 过滤器执行顺序,值越小,优先级越高return 0;}
}

自定义GatewayFilter

过滤器还可以支持动态配置参数,不过实现起来比较复杂

@Component
public class PrintAnyGatewayFilterFactory // 父类泛型是内部类的Config类型extends AbstractGatewayFilterFactory<PrintAnyGatewayFilterFactory.Config> {@Overridepublic GatewayFilter apply(Config config) {// OrderedGatewayFilter是GatewayFilter的子类,包含两个参数:// - GatewayFilter:过滤器// - int order值:值越小,过滤器执行优先级越高return new OrderedGatewayFilter(new GatewayFilter() {@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {// 获取config值String a = config.getA();String b = config.getB();String c = config.getC();// 编写过滤器逻辑System.out.println("a = " + a);System.out.println("b = " + b);System.out.println("c = " + c);// 放行return chain.filter(exchange);}}, 100);}// 自定义配置属性,成员变量名称很重要,下面会用到@Datastatic class Config{private String a;private String b;private String c;}// 将变量名称依次返回,顺序很重要,将来读取参数时需要按顺序获取@Overridepublic List<String> shortcutFieldOrder() {return List.of("a", "b", "c");}// 返回当前配置类的类型,也就是内部的Config@Overridepublic Class<Config> getConfigClass() {return Config.class;}}

然后在yaml文件中使用

spring:cloud:gateway:default-filters:- PrintAny=1,2,3 # 注意,这里多个参数以","隔开,将来会按照shortcutFieldOrder()方法返回的参数顺序依次复制

3、登录校验(网关传递用户信息)

调用的消费者是从网关开始发起的

总体流程图
在这里插入图片描述

准备好jwt相关的代码ceatToken,和parseToken等

编写登录校验过滤器

@Component
@RequiredArgsConstructor
@EnableConfigurationProperties(AuthProperties.class)
public class AuthGlobalFilter implements GlobalFilter, Ordered {private final JwtTool jwtTool;private final AuthProperties authProperties;private final AntPathMatcher antPathMatcher = new AntPathMatcher();@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {// 1.获取RequestServerHttpRequest request = exchange.getRequest();// 2.判断是否不需要拦截if(isExclude(request.getPath().toString())){// 无需拦截,直接放行return chain.filter(exchange);}// 3.获取请求头中的tokenString token = null;List<String> headers = request.getHeaders().get("authorization");if (!CollUtils.isEmpty(headers)) {token = headers.get(0);}// 4.校验并解析tokenLong userId = null;try {userId = jwtTool.parseToken(token);} catch (UnauthorizedException e) {// 如果无效,拦截ServerHttpResponse response = exchange.getResponse();response.setRawStatusCode(401);return response.setComplete();}// TODO 5.如果有效,传递用户信息,放在下面了System.out.println("userId = " + userId);// 6.放行return chain.filter(exchange);}private boolean isExclude(String antPath) {for (String pathPattern : authProperties.getExcludePaths()) {if(antPathMatcher.match(pathPattern, antPath)){return true;}}return false;}@Overridepublic int getOrder() {return 0;}
}

我们修改登录校验拦截器的处理逻辑,保存用户信息到请求头中:
在这里插入图片描述

编辑拦截器

public class UserInfoInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {// 1.获取请求头中的用户信息String userInfo = request.getHeader("user-info");// 2.判断是否为空if (StrUtil.isNotBlank(userInfo)) {// 不为空,保存到ThreadLocalUserContext.setUser(Long.valueOf(userInfo));}// 3.放行return true;}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {// 移除用户UserContext.removeUser();}
}

配置登录拦截器

@Configuration
//利用此处自动装配,区分网关和springmvc引用
@ConditionalOnClass(DispatcherServlet.class)
public class MvcConfig implements WebMvcConfigurer {@Overridepublic 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

最后获取用户id

微服务就可以使用ThreadLocal获取对应的用户id进行操作

4、登录校验(openFeign传递用户信息)

调用的消费者是从其他微服务发起的
在这里插入图片描述

@Bean
public RequestInterceptor userInfoRequestInterceptor(){return new RequestInterceptor() {@Overridepublic void apply(RequestTemplate template) {// 获取登录用户Long userId = UserContext.getUser();if(userId == null) {// 如果为空则直接跳过return;}// 如果不为空则放入请求头中,传递给下游微服务,存信息template.header("user-info", userId.toString());}};
}

整体流程
在这里插入图片描述
说明:当一个微服务调用另一个微服务时候,利用openFeign的RequestInterceptor 拦截器,将请求头为user-info,值为用户id存起来,
这个时候,请求别的微服务的时候,就进入微服务的拦截器HandlerInterceptor ,此时就可以再次拿出请求头信息,存在ThreadLocal中,然后调用其他服务就可以将用户信息传递过去,详细见上图。

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 【Kotlin设计模式】Kotlin实现工厂模式
  • 【WPF】WPF学习之面试常问问题
  • Visual Studio中 自动生成版本号递增版本号
  • React 入门第四天:理解React中的路由与导航
  • 【C#】字段
  • 点晴oa办公系统提效管理+业务协同
  • 极光公布2024年第二季度财报
  • MYSQL -NATURAL JOIN ,单行函数
  • FFmpeg的入门实践系列四(AVS)
  • 给鼠标一个好看的指针特效 鼠标光标如何修改形状?
  • Cisco-综合实验二
  • Linux--NAT,代理服务,内网穿透
  • Python网络爬虫模拟登录与验证解析
  • 为什么要学习 CCRC-PIPP
  • 若依后端 MyBatis改为MyBatis-Plus
  • 2017-09-12 前端日报
  • 4. 路由到控制器 - Laravel从零开始教程
  • Codepen 每日精选(2018-3-25)
  • Docker 1.12实践:Docker Service、Stack与分布式应用捆绑包
  • download使用浅析
  • ES6 学习笔记(一)let,const和解构赋值
  • ES6之路之模块详解
  • JavaScript-Array类型
  • JavaScript中的对象个人分享
  • JDK 6和JDK 7中的substring()方法
  • JS专题之继承
  • Making An Indicator With Pure CSS
  • MySQL QA
  • Octave 入门
  • Python3爬取英雄联盟英雄皮肤大图
  • Sublime text 3 3103 注册码
  • Vim 折腾记
  • vue的全局变量和全局拦截请求器
  • 不上全站https的网站你们就等着被恶心死吧
  • 产品三维模型在线预览
  • 当SetTimeout遇到了字符串
  • 浮现式设计
  • 基于Dubbo+ZooKeeper的分布式服务的实现
  • 实战|智能家居行业移动应用性能分析
  • 事件委托的小应用
  • 手机端车牌号码键盘的vue组件
  • 推荐一款sublime text 3 支持JSX和es201x 代码格式化的插件
  • 一起来学SpringBoot | 第三篇:SpringBoot日志配置
  • 异步
  • ​LeetCode解法汇总1276. 不浪费原料的汉堡制作方案
  • ​人工智能书单(数学基础篇)
  • #微信小程序:微信小程序常见的配置传旨
  • (1)安装hadoop之虚拟机准备(配置IP与主机名)
  • (3)Dubbo启动时qos-server can not bind localhost22222错误解决
  • (C语言)深入理解指针2之野指针与传值与传址与assert断言
  • (zt)基于Facebook和Flash平台的应用架构解析
  • (备份) esp32 GPIO
  • (第三期)书生大模型实战营——InternVL(冷笑话大师)部署微调实践
  • (附源码)springboot 校园学生兼职系统 毕业设计 742122
  • (四)js前端开发中设计模式之工厂方法模式