Gateway:网关路由与登录鉴权
在微服务架构中,用户登录和身份校验的处理方式确实与单体应用有所不同。在单体架构中,一旦用户通过身份验证,其会话信息可以在整个应用范围内共享,所有模块都能访问到用户信息。然而,在微服务架构下,每个服务独立部署且通常运行在不同的进程中,因此需要一种机制来确保用户的身份信息能够在各个微服务之间安全、高效地传递和验证。
目录
网关实现路由
网关登录鉴权
鉴权思路
登录校验流程图
网关过滤器详解
实现网关过滤器
拦截器流程图
服务信息鉴权
网关实现路由
问题:每个微服务都有不同的地址或端口,入口不同,请求不同数据时要访问不同的入口,需要维护多个入口地址,前端无法调用nacos,无法实时更新服务列表。
解决方案:采用微服务网关,数据在网络间传输,从一个网络传输到另一网络时就需要经过网关来做数据的路由和转发。
在微服务中新建一个网关模块,作为网关微服务
引入依赖(需要加入nacos依赖,让nacos管理)
<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>
在application.yaml文件,配置路由
gateway:routes:- id: item # 路由规则id,自定义,唯一uri: lb://item-service # 路由的目标服务,lb代表负载均衡,会从注册中心拉取服务列表predicates: # 路由断言,判断当前请求是否符合当前规则,符合则路由到目标服务- Path=/items/**,/search/** # 这里是以请求路径作为判断规则
经测试成功
网关登录鉴权
问题:单体架构时我们只需要完成一次用户登录、身份校验,就可以在所有业务中获取到用户信息。但是,在微服务中,每个微服务都独立部署,一般只有用户微服务能校验登录信息,事实上,这是不安全的。
解决方案:既然网关是所有微服务的入口,一切请求都需要先经过网关。我们完全可以把登录校验的工作放到网关去做。
鉴权思路
登录是基于JWT来实现的,校验JWT的算法复杂,而且需要用到秘钥。我们不可能让个微服务都需要知道JWT的秘钥,不安全。也不可能每个微服务重复编写登录校验代码、权限校验代码,麻烦。
所以,我们在网关和用户服务保存秘钥,开发登录校验功能。
登录校验流程图

我们可以看到:前端——网关——后端服务
我们在网关层去实现过滤请求。
网关过滤器详解
Gateway内部工作的基本原理:

如图所示:
-
客户端请求进入网关后由
HandlerMapping对请求做判断,找到与当前请求匹配的路由规则(Route),然后将请求交给WebHandler去处理。 -
WebHandler则会加载当前路由下需要执行的过滤器链(Filter chain),然后按照顺序逐一执行过滤器(后面称为Filter)。 -
图中
Filter被虚线分为左右两部分,是因为Filter内部的逻辑分为pre和post两部分,分别会在请求路由到微服务之前和之后被执行。 -
只有所有
Filter的pre逻辑都依次顺序执行通过后,请求才会被路由到微服务。 -
微服务返回结果后,再倒序执行
Filter的post逻辑。 -
最终把响应结果返回。
反正就是:我们需要在NettyRoutingFilter过滤器之前,在发起Request时,即pre时,定义一个过滤器,进行网关登录校验。
实现网关过滤器
我们采用全局过滤器,作用范围是所有路由,即GlobalFilter。
package com.hmall.gateway.filters;import com.hmall.common.exception.UnauthorizedException;
import com.hmall.gateway.config.AuthProperties;
import com.hmall.gateway.utils.JwtTool;
import lombok.RequiredArgsConstructor;
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;@RequiredArgsConstructor
@Component
public class AuthGlobalFilter implements GlobalFilter, Ordered {private final AuthProperties authProperties;private final JwtTool jwtTool;private final AntPathMatcher antPathMatcher = new AntPathMatcher();/*** 过滤器* @param exchange* @param chain* @return*/@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {// 1.获取请求对象ServerHttpRequest 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 (headers != null && !headers.isEmpty()) {token = headers.get(0);}// 4.解析tokenLong userId = null;try {userId = jwtTool.parseToken(token);} catch (UnauthorizedException e) {// 如果无效,拦截ServerHttpResponse response = exchange.getResponse();response.setStatusCode(HttpStatus.UNAUTHORIZED);return response.setComplete();}// 5.获取用户信息String userInfo = userId.toString();ServerWebExchange swe = exchange.mutate().request(builder -> builder.header("user-info",userInfo )).build();// 6.放行return chain.filter(swe);}/*** 判断路径是否需要拦截* @param antPath* @return*/private boolean isExclude(String antPath) {for (String pathPattern : authProperties.getExcludePaths()) {if(antPathMatcher.match(pathPattern, antPath)){return true;}}return false;}// 优先级@Overridepublic int getOrder() {return 0;}
}
因为我采用了JWT令牌进行校验,所以引入了JWT工具类,进行令牌的获取与解析。
至于AuthProperties是配置了不需要鉴权就能访问的路径。
经测试,网关已经可以完成登录校验并获取登录用户身份信息。
问题:当网关将请求转发到微服务时,微服务如何获取用户身份,我们不可能每个微服务都写一个拦截器去得到用户身份信息。
解决方案:将用户信息以请求头的方式传递到下游微服务。然后微服务可以从请求头中获取登录用户信息(上述代码第五步已经完成了)。考虑到微服务内部可能很多地方都需要用到登录用户信息,因此我们可以利用SpringMVC的拦截器来实现登录用户信息获取,并存入ThreadLocal。
拦截器流程图

提供一个用于保存登录用户的ThreadLocal工具UserContext:
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();}
}
在公用模块下定义一个拦截器:
public class UserInfoInterceptor implements HandlerInterceptor {/*** 请求拦截器* @param request* @param response* @param handler* @return* @throws Exception*/@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {// 1.获取请求头中的 用户信息String userInfo = request.getHeader("user-info");// 2.判断是否为空if (StringUtils.isNotBlank(userInfo)) {UserContext.setUser(Long.valueOf(userInfo));}// 3.放行return true;}/*** 响应拦截器* @param request* @param response* @param handler* @param ex* @throws Exception*/@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {UserContext.removeUser();}
}
编写SpringMVC的配置类,配置登录拦截器:
@Configuration
@ConditionalOnClass(DispatcherServlet.class)
public class MvcConfig implements WebMvcConfigurer {public void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(new UserInfoInterceptor());}
}
注意:这个配置类默认是不会生效的,基于SpringBoot的自动装配原理,我们要将其添加到resources目录下的META-INF/spring.factories文件中:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\com.hmall.common.config.MyBatisConfig,\com.hmall.common.config.MvcConfig
经测试,服务得到网关传递的用户身份信息。
服务信息鉴权
问题:因为之前编写的过滤器和拦截器功能,微服务可以轻松获取登录用户信息。但是,有时候请求到达微服务后还需要调用其它多个微服务。我们没有实现服务之间的用户身份信息的传递。
解决方案:由于微服务获取用户信息是通过拦截器在请求头中读取,因此要想实现微服务之间的用户信息传递,就必须在微服务发起调用时把用户信息存入请求头。
因为我们之前微服务之间调用是基于OpenFeign来实现的,并不是我们自己发送的请求。所以我们可以采用Feign中提供的一个拦截器接口:feign.RequestInterceptor。
在公用模块下定义一个userInfoRequestInterceptor
/*** feign请求拦截器 微服务之间的远程调用时,将当前登录用户的userId传递给目标服务* @return*/@Beanpublic RequestInterceptor userInfoRequestInterceptor() {return new RequestInterceptor() {public void apply(RequestTemplate requestTemplate) {Long userId = UserContext.getUser();if (userId != null) {requestTemplate.header("user-info", userId.toString());}}};}
总结:
- 为了实现网关处简便的登录校验,我们采用了GlobalFilter;
- 为了实现网关传递用户信息到多个微服务,我们采用了UserInfoInterceptor ;
- 为了实现微服务之间用户身份信息传递,我们采用了userInfoRequestInterceptor。
相关文章:
Gateway:网关路由与登录鉴权
在微服务架构中,用户登录和身份校验的处理方式确实与单体应用有所不同。在单体架构中,一旦用户通过身份验证,其会话信息可以在整个应用范围内共享,所有模块都能访问到用户信息。然而,在微服务架构下,每个服…...
本地部署DeepSeek R1大数据模型知识库
DeepSeek-V3 的综合能力 DeepSeek-V3 在推理速度上相较历史模型有了大幅提升。在目前大模型主流榜单中,DeepSeek-V3 在开源模型中位列榜首,与世界上最先进OpenAI 闭源模型不分伯仲。 1、下载Ollama运行大数据库 Ollama支持 Llama 3.3, DeepSeek-R1, Phi-…...
使用 Python 开发的简单招聘信息采集系统
以下是一个使用 Python 开发的简单招聘信息采集系统,它包含用户登录、招聘信息收集和前后端交互的基本功能。我们将使用 Flask 作为后端框架,HTML 作为前端页面。 项目结构 recruitment_system/ ├── app.py ├── templates/ │ ├── login.html │ ├── index…...
【jstack查询线程信息】1.对比下arthas的thread 和jvm指令
1)jps拿到进程号 2)jstack <pid> > <xxx.txt> // jstack作用:分析线程信息,死循环,死锁 jstack 23647 > 23647.txt Found 1 deadlock 3)对比:arthas查看线程信息 [arthas68751]$ thread -n 10 "MainWorker" Id69 cpuUsage72.29% deltaTime156ms …...
苦瓜书盘官网,免费pdf/mobi电子书下载网站
苦瓜书盘(kgbook)是一个专注于提供6英寸PDF和MOBI格式电子书的免费下载平台,专为电子阅读器用户设计。该平台为用户提供了丰富的电子书资源,涵盖文学、历史、科学、技术等多个领域,旨在打造一个全面的电子书资源库。用…...
【AI】AI开源IDE:CLine源码分析报告
1. 源码位置: CLine 是一个开源的 VSCode 插件,其完整源码托管在 GitHub 的 cline/cline 仓库中。这个仓库包含 CLine 的核心逻辑(TypeScript 编写),包括与 LLM 的对话控制、工具调用接口,以及 VSCode 插件…...
Nacos学习笔记-占位符读取其他命名空间内容
Nacos当前命名空间下的配置文件需要跨命名空间读取其他配置文件的内容。可以先通过Nacos提供的API接口获取配置文件内容,然后解析数据将其放入环境的PropertySource中。 相关依赖包 <!-- Nacos依赖包 --> <dependency><groupId>com.alibaba.clo…...
HarmonyOS 音频录制与播放模块
HarmonyOS 音频录制与播放模块 1.模块功能概览 麦克风权限动态检测与申请音频录制功能(支持参数配置)音频波形实时可视化(暂时未完善,先凑合看,后续会完善)录音文件播放功能 2.权限检测流程 1.代码实现…...
小白学Agent技术[4](Agent设计模式)
文章目录 Agent设计模式Zero shotFew shot应用场景 技术特性对比ReAct模式ReAct模式简介ReAct模式举例ReAct模式实现 Plan and Solve模式实现原理 Reason without Observation模式LLMCompiler模式实现原理 Basic ReflectionBasic Reflection原理 Reflexion 模式Reflexion 模式原…...
Google参数逆向 谷歌搜索
背景 从2025年1月15日开始,Google强制用户开启JavaScript才能使用Google搜索功能。 由于此前业务需要调用Google搜索功能去寻找目标资源进行下载,Google的改版导致整条产线全部瘫痪,急需解决方案。 业务每日需要Google搜索数百万次 临时解决方…...
OneM2M:全球性的物联网标准-可应用于物联网中
OneM2M 是一个全球性的物联网(IoT)标准,旨在为物联网设备和服务提供统一的框架和接口,以实现设备之间的互操作性、数据共享和服务集成。OneM2M 由多个国际标准化组织(如 ETSI、TIA、TTC、ARIB 等)共同制定,目标是解决物联网领域的碎片化问题,提供一个通用的标准,支持跨…...
MySQL环境搭建和基本操作
前言 MySQL是现在最为流行的数据库,而且是开源的,任何人都可以在Internet下载,进行安装。 MySQL环境搭建 一、软件包安装 MySQL是目前最为流行的开放源码的数据库,是完全网络化的跨平台的关系型数据库系统,它是由瑞典MySQLAB公司…...
【GIT】non-fast-forward错误
遇到 non-fast-forward 错误时,通常是因为远程仓库有本地尚未包含的提交(如远程仓库初始化时自动生成的 README.md 等文件)。以下是分步解决方案: 1. 拉取远程更改并合并历史 git pull origin master --allow-unrelated-historie…...
深入了解Linux —— 调试程序
前言 我们已经学习了linux下许多的工具,vim、gcc、make/makefile等; 已经能够在linux写代码,并且进行编译运行,让程序在linux下跑起来。 但是,如果我们在写代码的时候遇见了错误;但是我们并不知道错误在哪&…...
JVM - 3.垃圾回收
1.垃圾收集的经典问题 1.哪些内存需要回收2.什么时候回收3.如何回收1.你知道哪几种垃圾回收器,各自的优缺点,重点讲一下cms和g12.JVM GC算法有哪些,目前的JDK版本采用什么回收算法3.G1回收器的回收过程 1.Java中垃圾的定义(Garbag…...
vs code 设置字体颜色
修改setting.json文件 {"remote.SSH.remotePlatform": {"ubuntu": "linux"},// "workbench.colorTheme": "One Dark Pro",// "editor.semanticTokenColorCustomizations": {// },"editor.semanticTokenColo…...
MoonSharp 文档一
目录 1.Getting Started(入门手册) 步骤1:在 IDE 中引入 MoonSharp 步骤2:引入命名空间 步骤3:调用脚本 步骤4:运行代码 2.Keeping a Script around(保留一个脚本) 步骤1:复现前教程所有操作 步骤2:改为创建Script对象 步骤3:访问全局环境 步骤4:直接调用…...
Unity3D 图形渲染(Graphics Rendering)详解
前言 Unity3D 是一款广泛使用的游戏引擎,其图形渲染系统是开发者创建高质量视觉效果的核心。本文将深入探讨 Unity3D 的图形渲染管线、渲染技术、以及如何通过代码实现自定义渲染效果。 对惹,这里有一个游戏开发交流小组,大家可以点击进来一…...
计算机视觉图像点运算【灰度直方图均衡化图形界面实操理解 +开源代码】
对一个数字图像处理系统来说,一般的处理过程为三个步骤:图像预处理、特征抽取、图像识别和分析。图像的点运算就是预处理过程中的重要一步,点运算是对图像的灰度级进行变换。 图像点运算概念 点运算是指对图像的每个像素依次进行相同的灰度变…...
在Windows 7操作系统,基于llama.cpp本地化部署 deepseek-r1模型的方法 2025-02-08
一、概述 现在已经是大模型时代。 个人认为,deepseek效果惊艳,大模型已进入实用阶段。 有些电脑,由于种种原因,还在用 Windows 7, Windows XP 等操作系统。 为了让这些电脑用上大模型,本教程在 llama.c…...
【ROS】Nav2源码之nav2_behavior_tree-行为树节点列表
1、行为树节点分类 在 Nav2(Navigation2)的行为树框架中,行为树节点插件按照功能分为 Action(动作节点)、Condition(条件节点)、Control(控制节点) 和 Decorator(装饰节点) 四类。 1.1 动作节点 Action 执行具体的机器人操作或任务,直接与硬件、传感器或外部系统…...
苍穹外卖--缓存菜品
1.问题说明 用户端小程序展示的菜品数据都是通过查询数据库获得,如果用户端访问量比较大,数据库访问压力随之增大 2.实现思路 通过Redis来缓存菜品数据,减少数据库查询操作。 缓存逻辑分析: ①每个分类下的菜品保持一份缓存数据…...
让AI看见世界:MCP协议与服务器的工作原理
让AI看见世界:MCP协议与服务器的工作原理 MCP(Model Context Protocol)是一种创新的通信协议,旨在让大型语言模型能够安全、高效地与外部资源进行交互。在AI技术快速发展的今天,MCP正成为连接AI与现实世界的重要桥梁。…...
零基础在实践中学习网络安全-皮卡丘靶场(第九期-Unsafe Fileupload模块)(yakit方式)
本期内容并不是很难,相信大家会学的很愉快,当然对于有后端基础的朋友来说,本期内容更加容易了解,当然没有基础的也别担心,本期内容会详细解释有关内容 本期用到的软件:yakit(因为经过之前好多期…...
Spring Cloud Gateway 中自定义验证码接口返回 404 的排查与解决
Spring Cloud Gateway 中自定义验证码接口返回 404 的排查与解决 问题背景 在一个基于 Spring Cloud Gateway WebFlux 构建的微服务项目中,新增了一个本地验证码接口 /code,使用函数式路由(RouterFunction)和 Hutool 的 Circle…...
GO协程(Goroutine)问题总结
在使用Go语言来编写代码时,遇到的一些问题总结一下 [参考文档]:https://www.topgoer.com/%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B/goroutine.html 1. main()函数默认的Goroutine 场景再现: 今天在看到这个教程的时候,在自己的电…...
苹果AI眼镜:从“工具”到“社交姿态”的范式革命——重新定义AI交互入口的未来机会
在2025年的AI硬件浪潮中,苹果AI眼镜(Apple Glasses)正在引发一场关于“人机交互形态”的深度思考。它并非简单地替代AirPods或Apple Watch,而是开辟了一个全新的、日常可接受的AI入口。其核心价值不在于功能的堆叠,而在于如何通过形态设计打破社交壁垒,成为用户“全天佩戴…...
Java 与 MySQL 性能优化:MySQL 慢 SQL 诊断与分析方法详解
文章目录 一、开启慢查询日志,定位耗时SQL1.1 查看慢查询日志是否开启1.2 临时开启慢查询日志1.3 永久开启慢查询日志1.4 分析慢查询日志 二、使用EXPLAIN分析SQL执行计划2.1 EXPLAIN的基本使用2.2 EXPLAIN分析案例2.3 根据EXPLAIN结果优化SQL 三、使用SHOW PROFILE…...
PH热榜 | 2025-06-08
1. Thiings 标语:一套超过1900个免费AI生成的3D图标集合 介绍:Thiings是一个不断扩展的免费AI生成3D图标库,目前已有超过1900个图标。你可以按照主题浏览,生成自己的图标,或者下载整个图标集。所有图标都可以在个人或…...
webpack面试题
面试题:webpack介绍和简单使用 一、webpack(模块化打包工具)1. webpack是把项目当作一个整体,通过给定的一个主文件,webpack将从这个主文件开始找到你项目当中的所有依赖文件,使用loaders来处理它们&#x…...
