微服务之网关、网关路由、网关登录校验

简介:来源:SpringCloud微服务开发与实战,java黑马商城项目微服务实战开发(涵盖MybatisPlus、Docker、MQ、ES、Redis高级等)

认识网关

前端请求不能直接访问微服务,而是要请求网关:

  • 网关可以做安全控制,也就是登录身份校验,校验通过才放行
  • 通过认证后,网关再根据请求判断应该访问哪个微服务,将请求转发过去
    在这里插入图片描述
    在SpringCloud当中,提供了两种网关实现方案:
  • Netflix Zuul:早期实现,目前已经淘汰
  • SpringCloudGateway:基于Spring的WebFlux技术,完全支持响应式编程,吞吐能力更强。
    SpringCloudGateway官网:https://spring.io/projects/spring-cloud-gateway#learn
    在这里插入图片描述

网关路由-快速入门

接下来,我们先看下如何利用网关实现请求路由。由于网关本身也是一个独立的微服务,因此也需要创建一个模块开发功能。大概步骤如下:

  • 创建网关微服务
  • 引入SpringCloudGateway、NacosDiscovery依赖
  • 编写启动类
  • 配置网关路由

1、创建项目
首先,我们要创建一个新的module,比如命名为gateway,作为网关微服务:

2、引入依赖
在gateway模块的pom.xml文件中引入依赖

        <dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-gateway</artifactId></dependency>

3、编写启动类
在gateway模块中新建一个启动类

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

4、配置路由
接下来,在gateway模块的resources目录新建一个application.yaml文件,内容如下:

server:port: 8080
spring:application:name: gatewaycloud:nacos:server-addr: 192.168.254.129:8848gateway: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/**

网关路由-路由属性

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

  • id:路由唯一标示
  • uri:路由目标地址
  • predicates: 路由断言,判断请求是否符合当前路由
  • filters:路由过滤器,对请求或响应做特殊处理

Spring Cloud Gateway官网:https://docs.spring.io/spring-cloud-gateway/docs/3.1.9/reference/html/

1、路由断言
12种Route Predicate Factories实现示例
在这里插入图片描述
2、路由过滤器
33种路由过滤器官网实现示例
在这里插入图片描述
在default-filters下设置的过滤器对所有路由生效:

spring:cloud:gateway:routes:- id: itemuri: lb://item-servicepredicates:- Path=/items/**,/search/**default-filters:- AddRequestHeader=truth, anyone long-press like button will be rich

网关登录校验-思路分析

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

注意:NettyRoutingFilter是做请求转发的,所以应该在NettyRoutingFilter之前且在过滤器的pre逻辑之前进行登录校验

网关登录校验-自定义过滤器

在这里插入图片描述
自定义GlobalFilter过滤器

进行登录校验一般都选择自定义GlobalFilter过滤器

在这里插入图片描述
在这里插入图片描述
自定义GagtewayFilter过滤器

自定义GagtewayFilter过滤器比较麻烦,大多数情况都选择自定义GlobalFilter过滤器

自定义GatewayFilter不是直接实现GatewayFilter,而是实现AbstractGatewayFilterFactory。最简单的方式是这样的:

@Component
public class PrintAnyGatewayFilterFactory extends AbstractGatewayFilterFactory<Object> {@Overridepublic GatewayFilter apply(Object config) {return new GatewayFilter() {@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {// 获取请求ServerHttpRequest request = exchange.getRequest();// 编写过滤器逻辑System.out.println("过滤器执行了");// 放行return chain.filter(exchange);}};}
}

注意:该类的名称一定要以GatewayFilterFactory为后缀!

然后在yaml配置中这样使用:

spring:cloud:gateway:default-filters:- PrintAny # 此处直接以自定义的GatewayFilterFactory类名称前缀类声明过滤器

另外,这种过滤器还可以支持动态配置参数,不过实现起来比较复杂,示例:

@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()方法返回的参数顺序依次复制

上面这种配置方式参数必须严格按照shortcutFieldOrder()方法的返回参数名顺序来赋值。
还有一种用法,无需按照这个顺序,就是手动指定参数名:

spring:cloud:gateway:default-filters:- name: PrintAnyargs: # 手动指定参数名,无需按照参数顺序a: 1b: 2c: 3

网关登录校验-网关实现登录校验(网关到微服务的用户传递)

整体实现思路:
在这里插入图片描述

一、在网关的登录校验过滤器中,将从token解析出来的用户Id写入请求头并传递到微服务:
需求:修改gateway模块中的登录校验拦截器,在校验成功后保存用户到下游请求的请求头中
提示: 要修改转发到微服务的请求,需要用到ServerWebExchange类提供的API,示例如下:

package com.hmall.gateway.filter;import com.hmall.common.exception.UnauthorizedException;
import com.hmall.common.utils.CollUtils;
import com.hmall.gateway.config.AuthProperties;
import com.hmall.gateway.utils.JwtTool;
import lombok.AllArgsConstructor;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;import java.util.List;@Component
@RequiredArgsConstructor
public class AuthGlobalFilter implements GlobalFilter, Ordered {@Autowiredprivate AuthProperties authProperties;@Autowiredprivate JwtTool jwtTool;private final AntPathMatcher antPathMatcher = new AntPathMatcher();@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {//1、获取requestServerHttpRequest request = exchange.getRequest();//2、判断是否需要做登录拦截(即判断此请求路径是否在hm.auth的排除路径里)(排除路径就是:不需要登录就可以访问的路径)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){//拦截,设置响应状态码为401ServerHttpResponse response = exchange.getResponse();response.setStatusCode(HttpStatus.UNAUTHORIZED);return response.setComplete();}//TODO 5、传递用户信息String userInfo = userId.toString();ServerWebExchange swe = exchange.mutate().request(builder -> builder.header("user-info", userInfo)).build();//6、放行return chain.filter(swe);}private boolean isExclude(String path) {for (String pathPattern : authProperties.getExcludePaths()) {if (antPathMatcher.match(pathPattern,path)){return true;}}return false;}@Overridepublic int getOrder() {//过滤器执行顺序,值越小,优先级越高return 0;}
}

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

拦截器类实现:

package com.hmall.common.interceptors;import cn.hutool.core.util.StrUtil;
import com.hmall.common.utils.UserContext;
import org.springframework.web.servlet.HandlerInterceptor;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;//只用来获取登录用户的信息,不需要做登录拦截,因为真正的登录拦截校验已经在网关做过了
public class UserInfoInterceptor implements HandlerInterceptor {@Override//preHandle是在Controller之前执行public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {// 1.获取请求头中的用户信息String userInfo = request.getHeader("user-info");// 2.判断是否为空if (StrUtil.isNotBlank(userInfo)) {//StrUtil.isNotBlank(userInfo):判断userInfo字符串不能为null且不能为空// 不为空,保存到ThreadLocalUserContext.setUser(Long.valueOf(userInfo));//UserContext是ThreadLocal工具类,方便调用}// 3.放行return true;}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {// 移除用户UserContext.removeUser();//在Controller执行完后一定要清理用户}
}

ThreadLocal工具类实现:

package com.hmall.common.utils;public class UserContext {private static final ThreadLocal<Long> tl = new ThreadLocal<>();/*** 保存当前登录用户信息到ThreadLocal* @param userId 用户id*/public static void setUser(Long userId) {tl.set(userId);}/*** 获取当前登录用户信息* @return 用户id*/public static Long getUser() {return tl.get();}/*** 移除当前登录用户信息*/public static void removeUser(){tl.remove();}
}

注意:Spring MVC的拦截器要想生效,必须要进行配置,也就是需要注册
重点:SpringBoot自动装配原理:要想使在不同包路径下的Spring扫不到的那些配置类生效**,则需要在resource目录下的META-INF包下定义一个spring.factories文件去记录这些配置类
扩展: 在新版本的springboot中spring.factories文件名已经改了。

配置类实现:

package com.hmall.common.config;import com.hmall.common.interceptors.UserInfoInterceptor;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.DispatcherServlet;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;@Configuration
@ConditionalOnClass(DispatcherServlet.class)
public class MvcConfig implements WebMvcConfigurer {@Overridepublic 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

可以遇到的问题与报错:
①因为此模块是common公共模块,在微服务中网关模块pom.xml也可能需要引入common公共模块依赖; ②又因为WebMvcConfigurer属于spring mvc包下的,而网关不是SpringMvc环境的,而是一种非阻塞式的响应式编程。
所以为了避免冲突,使此配置类只在微服务生效而不在网关生效 。
解决方法
关键:Springboot自动装配原理是可以带条件的。
方法:在配置类中加一个条件@ConditionalOnClass(DispatcherServlet.class)
详解:DispatcherServlet.class(SpringMvc的核心API)判断此类在此环境中是否存在,存在则生效;这样就可以使此配置类只在微服务生效而不在网关生效,避免了冲突。

知识扩展:
@ConditionalOnClass(DispatcherServlet.class) 注解是 Spring Boot 的条件化配置注解之一。它的作用是使得该配置类(MvcConfig)只在 DispatcherServlet 类存在于类路径中时才会被加载。这意味着:

  1. 条件化加载:如果项目中没有 DispatcherServlet 类,Spring Boot 就不会加载这个配置类。通常,DispatcherServlet 是 Spring MVC 的核心组件,因此这个条件常用于确保该配置只在启用了 Spring MVC 的环境下生效。

  2. 用于模块化:通过这种方式,你可以确保配置类只在需要时才会被创建,从而减少不必要的配置和开销。

  3. 增强灵活性:这种做法使得应用更具灵活性,因为你可以在不同的环境中选择性地加载不同的配置。
    总结:
    @ConditionalOnClass(DispatcherServlet.class) 的使用在这里确保只有在有 Spring MVC 环境时,相关的拦截器和配置才会被应用,避免在不需要 MVC 功能的情况下进行不必要的配置。

网关登录校验-OpenFeign传递用户信息(微服务之间的用户传递)

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

总结

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/58210.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

DDRPHY数字IC后端设计实现系列专题

在对 LPDDR3 物理层接口模块进行后端设计之前&#xff0c;需要对该模块的功能结 构以及后端物理设计流程的相关理论进行深入的分析和研究。本章第一节详细分 析了本次 LPDDR3 物理层接口模块的结构&#xff0c;为该模块的布图布局的合理规划奠 定了理论基础&#xff0c;并且分析…

python机器人编程——一种3D骨架动画逆解算法的启示(上)

目录 一、前言二、fabrik 算法三、python实现结论PS.扩展阅读ps1.六自由度机器人相关文章资源ps2.四轴机器相关文章资源ps3.移动小车相关文章资源ps3.wifi小车控制相关文章资源 一、前言 我们用blender等3D动画软件时&#xff0c;会用到骨骼的动画&#xff0c;通过逆向IK动力学…

大数据之VIP(Virtual IP,虚拟IP)负载均衡

VIP&#xff08;Virtual IP&#xff0c;虚拟IP&#xff09;负载均衡是一种在计算机网络中常用的技术&#xff0c;用于将网络请求流量均匀地分散到多个服务器上&#xff0c;以提高系统的可扩展性、可靠性和性能。以下是对VIP负载均衡的详细解释&#xff1a; 一、VIP负载均衡的基…

想要音频里的人声,怎么把音频里的人声和音乐分开?

在音频处理领域&#xff0c;将音频中的人声和音乐分开是一个常见需求&#xff0c;尤其对于音乐制作、影视后期以及个人娱乐应用来说&#xff0c;这种分离技术显得尤为重要。随着科技的发展&#xff0c;现在已经有多种方法可以实现这一目的。 一、使用专业音频处理软件 市面上有…

动态规划 —— 路径问题-不同路径 ||

1. 不同路径 || 题目链接&#xff1a; 63. 不同路径 II - 力扣&#xff08;LeetCode&#xff09;https://leetcode.cn/problems/unique-paths-ii/description/ 2. 算法原理 状态表示&#xff1a;以莫一个位置位置为结尾 dp[i]表示&#xff1a;以[i&#xff0c;j]位置为结尾时…

Telephony IMS

1、IMS结构 IMS的启动过程,IMS业务依附于phone进程启动,当phone进程启动时拉起对应的框架代码。 当phone进程启动时,会启动ImsReslover类,该类用于寻找对应的IMS apk实现,并进行绑定。 当绑定成功后,ImsServiceController会保存IMS APK中的如下对象IImsRegistration IIms…

Consul微服务配置中心部署(在线安装)

博主介绍&#xff1a; 计算机科班人&#xff0c;全栈工程师&#xff0c;掌握C、C#、Java、Python、Android等主流编程语言&#xff0c;同时也熟练掌握mysql、oracle、sqlserver等主流数据库&#xff0c;具有丰富的项目经验和开发技能。提供相关的学习资料、程序开发、技术解答、…

Spring Task—定时任务

Spring Task 是 Spring 提供的一种轻量级定时任务调度功能&#xff0c;内置在 Spring 框架中。与 Quartz 等重量级调度框架相比&#xff0c;Spring Task 使用简便&#xff0c;无需额外依赖&#xff0c;适合在简单的调度任务场景中使用。通过注解配置方式&#xff0c;开发者可以…

分布式数据库技术金融应用规范技术架构

目录 引 言 概述 技术框架 技术框架概述 物理资源层 计算模块 功能特性 功能架构概述 基本功能 部署灵活性 并发处理能力 扩缩容 扩容 缩容 引 言 随着金融领域分布式架构的转型升级&#xff0c;分布式数据库技术在金融领域应用逐步深入。为规范分布 式数据库技…

从0开始electron+vue2搭建环境

使用环境&#xff1a;node版本16.16.0 目录 搭建vue项目安装electron打包electron 搭建vue项目 已有vue2的环境直接进项安装electron步骤 没有的请先移动到这里查看 vue2脚手架搭建项目流程 我就不另外记录了 安装electron 直接运行 vue add electron-builder安装完成后&…

【脚本】B站视频AB复读

控制台输入如下代码&#xff0c;回车 const video document.getElementsByTagName("video")[0];//获取bpx-player-control-bottom-center容器,更改其布局方式const div document.getElementsByClassName("bpx-player-control-bottom-center")[0];div.sty…

源码复现detectron2时遇到的错误

说明&#xff1a;安装detectron2直接进到官网 detectron2源码 中找安装文档&#xff0c;安装安装文档一般没什么问题&#xff0c;但是我确实出现了问题&#xff0c;包括有&#xff1a; gcc版本太低&#xff1a;提示说需要gcc 9及以上的版本才可以pytorch版本不匹配&#xff1a…

【机器学习基础】激活函数

激活函数 1. Sigmoid函数2. Tanh&#xff08;双曲正切&#xff09;函数3. ReLU函数4. Leaky ReLU函数 1. Sigmoid函数 观察导数图像在我们深度学习里面&#xff0c;导数是为了求参数W和B&#xff0c;W和B是在我们模型model确定之后&#xff0c;找出一组最优的W和B&#xff0c;使…

ipad-make-sense:首个支持 iPad 的开源数据标注工具

在机器学习和人工智能快速发展的今天&#xff0c;高质量的数据标注工具变得越来越重要。然而&#xff0c;大多数现有的数据标注工具都局限于传统的桌面环境&#xff0c;无法适应现代移动办公的需求。今天&#xff0c;我要向大家介绍一个突破性的解决方案 —— ipad-make-sense&…

阳振坤:云时代数据库的思考 | OceanBase发布会实录

在2024 OceanBase 年度发布会中&#xff0c;OceanBase 的创始人与首席科学家阳振坤进行了《云时代数据库的思考》的主题分享。本文为演讲实录。 亲爱的朋友们&#xff0c;衷心感谢各位莅临今天的发布会现场。今天是一个云的时代&#xff0c;我想与大家分享&#xff0c;我对于云…

51单片机完全学习——DS18B20温度传感器

一、DS18B20数据手册解读 首先我们知道DS18B20使用的是单总线传输&#xff0c;默认情况下读出来的温度是12位的&#xff0c;我们这里只讨论外部电源供电这种情况。 有这张图片我们知道&#xff0c;12位温度的最小分辨率是10^-4次方&#xff0c;因此就是0.0625.我们只需要将最后…

STM32应用详解(12)使用I2C的main函数例程

文章目录 前言一、支持I2C总线的常见器件二、程序详解1.main函数2.读取传感器温度值的函数3.相关宏定义 前言 学习I2C总线。本文件主要分析main函数和LM75A驱动程序。了解器件驱动基本原理。 一、支持I2C总线的常见器件 (1)支持I2C总线的器件有很多&#xff0c;在开发板上支持…

redis详细教程(3.ZSet,Bitmap,HyperLogLog)

ZSet Redis 的 ZSet&#xff08;有序集合&#xff09;是一种特殊的数据类型&#xff0c;它允许存储一系列不重复的字符串元素&#xff0c;并为每个元素关联一个分数&#xff08;score&#xff09;。这个分数用于对集合中的元素进行排序。ZSet 的特点是&#xff1a; 唯一性&am…

Openlayers高级交互(11/20):显示带箭头的线段轨迹,箭头居中

本示例介绍如何在vue+openlayers项目中设置带有箭头的线段,箭头位于线段的中间位置。这里用到 forEachSegment 函数, 这个函数接受一个特征和一个回调函数作为参数。它遍历特征中的每个子线段,并调用回调函数传入子线段的中点坐标。 效果图 专栏名称内容介绍Openlayers基础…

Springboot整合spring-boot-starter-data-elasticsearch

前言 <font style"color:rgb(36, 41, 47);">spring-boot-starter-data-elasticsearch</font> 是 Spring Boot 提供的一个起始依赖&#xff0c;旨在简化与 Elasticsearch 交互的开发过程。它集成了 Spring Data Elasticsearch&#xff0c;提供了一套完整…