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

【Sa-Token|4】Sa-Token微服务项目应用

若微服务数量多,如果每个服务都改动,工作量大,则可以只在网关和用户中心进行改动,也是可以实现服务之间的跳转。
这种方式可以通过在网关服务中生成和验证 Sa-Token,并将其与现有的 Token关联存储在 Redis 中。用户中心提供额外的接口来验证和生成新的 Sa-Token。
这样只需要改造网关和用户服务,其他服务保持现状,即可实现各个应用之间的跳转

在这里插入图片描述

实现方案

为了实现这个目标,同时尽量减少对现有系统的改动,可以在网关和用户中心进行必要的改动,以确保用户在登录后能够在多个系统中无缝访问。

下面是详细的方案和步骤:

一、用户中心改造

用户中心需要提供两个接口:

  1. 登录接口:用户登录并生成原有业务系统的 Token。
  2. Token 验证和 Sa-Token 生成接口:验证原有 Token 并生成 Sa-Token。
1. 登录接口
@RestController
@RequestMapping("/auth")
public class AuthController {@PostMapping("/login")public ResponseEntity<Map<String, Object>> login(@RequestParam String username, @RequestParam String password) {Map<String, Object> result = new HashMap<>();// 进行用户名和密码校验if (checkCredentials(username, password)) {String originalToken = generateOriginalToken(username); // 生成原有业务系统的 Tokenresult.put("originalToken", originalToken);return ResponseEntity.ok(result);}result.put("error", "登录失败");return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(result);}private boolean checkCredentials(String username, String password) {// 检查用户名和密码逻辑return true; // 假设校验成功}private String generateOriginalToken(String username) {// 生成原有业务系统的 Tokenreturn "original-token";}
}
2. Token 验证和 Sa-Token 生成接口
@RestController
@RequestMapping("/auth")
public class AuthController {@Autowiredprivate RedisTemplate<String, String> redisTemplate;@GetMapping("/check")public ResponseEntity<SaResult> checkToken(@RequestParam String token) {// 验证原有 Tokenif (validateOriginalToken(token)) {String userId = getUserIdFromOriginalToken(token);StpUtil.login(userId);String saToken = StpUtil.getTokenValue();// 存储原有 Token 和 Sa-Token 的映射关系redisTemplate.opsForValue().set("sa-token:" + token, saToken);redisTemplate.opsForValue().set("original-token:" + saToken, token);return ResponseEntity.ok(SaResult.ok("Token 有效").set("saToken", saToken));}return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(SaResult.error("Token 无效"));}private boolean validateOriginalToken(String token) {// 验证原有业务系统的 Tokenreturn true; // 假设校验成功}private String getUserIdFromOriginalToken(String token) {// 从原有业务系统的 Token 中解析出用户IDreturn "parsed-user-id";}
}

二、网关服务改造

网关服务需要在所有请求到达后端服务前,进行原有 Token 的验证和 Sa-Token 的生成和验证。

1. 引入 Redis 依赖

确保网关服务的 pom.xml 文件中包含 Redis 依赖:

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
2. 配置 Redis

application.yml 文件中配置 Redis 连接信息:

spring:redis:host: localhostport: 6379password: ""
3. 网关服务 Token 校验过滤器
@Component
public class TokenFilter implements GlobalFilter, Ordered {@Autowiredprivate RestTemplate restTemplate;@Autowiredprivate RedisTemplate<String, String> redisTemplate;@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {String originalToken = exchange.getRequest().getHeaders().getFirst("Authorization");if (originalToken != null) {String saToken = redisTemplate.opsForValue().get("sa-token:" + originalToken);if (saToken == null) {// 调用用户中心的接口生成 Sa-TokensaToken = generateSaToken(originalToken);if (saToken == null) {exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);return exchange.getResponse().setComplete();}}exchange.getRequest().mutate().header("Sa-Token", saToken).build();return chain.filter(exchange);}exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);return exchange.getResponse().setComplete();}private String generateSaToken(String originalToken) {// 调用用户中心的接口生成 Sa-TokenString url = "http://user-center/auth/check?token=" + originalToken;ResponseEntity<SaResult> response = restTemplate.getForEntity(url, SaResult.class);if (response.getStatusCode() == HttpStatus.OK && response.getBody().getCode() == 200) {return response.getBody().get("saToken").toString();}return null;}@Overridepublic int getOrder() {return -100;}
}

三、工作流程

  1. 用户登录

    • 用户通过用户中心的登录接口进行登录,用户中心生成原有业务系统的 Token 并返回给前端。
  2. 请求网关

    • 前端在请求其他服务时,将原有的 Token 添加到请求头中,发送请求到网关。
  3. 网关处理

    • 网关拦截请求,从请求头中获取原有的 Token。
    • 网关调用用户中心的 Token 验证接口来验证原有 Token 并生成 Sa-Token。
    • 将原有 Token 和 Sa-Token 的映射关系存储在 Redis 中。
    • 将 Sa-Token 添加到请求头中,转发请求到后端服务。
  4. 后端服务

    • 后端服务可以继续保持现有的 Token 逻辑,不需要改动。所有的 Token 验证和生成逻辑都在网关和用户中心处理。

模块详细介绍

在上述方案中,实现单点登录的关键步骤包括:

  1. 用户中心提供接口验证原有 Token 并生成 Sa-Token:通过 /auth/check 接口,用户中心接收原有业务系统的 Token,验证其有效性并生成新的 Sa-Token。
  2. 网关服务拦截所有请求,验证和生成 Sa-Token:网关服务在接收到请求时,首先检查请求头中的原有 Token,并调用用户中心的 /auth/check 接口来验证 Token 并生成 Sa-Token,将生成的 Sa-Token 存储在 Redis 中,并添加到请求头中转发给后端服务。

这些步骤确保了用户在登录后,可以使用原有 Token 进行验证,并通过 Sa-Token 进行单点登录的验证,从而实现了单点登录的效果。

具体的实现过程

  1. 用户登录生成原有业务系统的 Token

    • 用户通过用户中心的登录接口进行登录。
    • 用户中心生成原有业务系统的 Token 并返回给前端。
  2. 网关服务处理用户请求

    • 前端在请求其他服务时,将原有的 Token 添加到请求头中,发送请求到网关。
    • 网关服务拦截请求,获取请求头中的原有 Token。
  3. 网关服务验证和生成 Sa-Token

    • 网关服务调用用户中心的 /auth/check 接口,验证原有 Token 的有效性。
    • 用户中心验证原有 Token 有效后,生成 Sa-Token 并返回给网关服务。
    • 网关服务将原有 Token 和 Sa-Token 的映射关系存储在 Redis 中。
    • 网关服务将生成的 Sa-Token 添加到请求头中,转发请求到后端服务。
  4. 后端服务处理请求

    • 后端服务可以继续保持现有的 Token 验证逻辑,不需要改动。
    • 所有的 Token 验证和生成逻辑都在网关和用户中心处理。

实现细节

1. 用户中心新增 Token 验证和生成 Sa-Token 接口
@RestController
@RequestMapping("/auth")
public class AuthController {@Autowiredprivate RedisTemplate<String, String> redisTemplate;@GetMapping("/check")public ResponseEntity<SaResult> checkToken(@RequestParam String token) {// 验证原有 Tokenif (validateOriginalToken(token)) {String userId = getUserIdFromOriginalToken(token);StpUtil.login(userId);String saToken = StpUtil.getTokenValue();// 存储原有 Token 和 Sa-Token 的映射关系redisTemplate.opsForValue().set("sa-token:" + token, saToken);redisTemplate.opsForValue().set("original-token:" + saToken, token);return ResponseEntity.ok(SaResult.ok("Token 有效").set("saToken", saToken));}return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(SaResult.error("Token 无效"));}private boolean validateOriginalToken(String token) {// 验证原有业务系统的 Tokenreturn true; // 假设校验成功}private String getUserIdFromOriginalToken(String token) {// 从原有业务系统的 Token 中解析出用户IDreturn "parsed-user-id";}
}
2. 网关服务 Token 校验过滤器
@Component
public class TokenFilter implements GlobalFilter, Ordered {@Autowiredprivate RestTemplate restTemplate;@Autowiredprivate RedisTemplate<String, String> redisTemplate;@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {String originalToken = exchange.getRequest().getHeaders().getFirst("Authorization");if (originalToken != null) {String saToken = redisTemplate.opsForValue().get("sa-token:" + originalToken);if (saToken == null) {// 调用用户中心的接口生成 Sa-TokensaToken = generateSaToken(originalToken);if (saToken == null) {exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);return exchange.getResponse().setComplete();}}exchange.getRequest().mutate().header("Sa-Token", saToken).build();return chain.filter(exchange);}exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);return exchange.getResponse().setComplete();}private String generateSaToken(String originalToken) {// 调用用户中心的接口生成 Sa-TokenString url = "http://user-center/auth/check?token=" + originalToken;ResponseEntity<SaResult> response = restTemplate.getForEntity(url, SaResult.class);if (response.getStatusCode() == HttpStatus.OK && response.getBody().getCode() == 200) {return response.getBody().get("saToken").toString();}return null;}@Overridepublic int getOrder() {return -100;}
}

工作流程总结

  1. 用户登录并获取原有业务系统的 Token。
  2. 用户在请求服务时,携带原有 Token 发送请求到网关。
  3. 网关验证原有 Token,并通过用户中心生成 Sa-Token。
  4. 网关将 Sa-Token 存储在 Redis 中,并添加到请求头中转发给后端服务。
  5. 后端服务保持现有逻辑不变,网关和用户中心负责所有的 Token 验证和生成。

通过以上步骤,达成了在现有系统中实现的目标,同时最大限度地减少了对现有系统的改动。

五、总结

通过上述详细的改造步骤和代码示例,可以在不改动后端服务的情况下,实现单点登录。所有的 Token
验证和生成逻辑都集中在网关和用户中心,实现了 Token 的统一管理和验证。这样既实现了项目之间跳转的目标,又最大限度地减少了对现有系统的改造。

相关文章:

【Sa-Token|4】Sa-Token微服务项目应用

若微服务数量多&#xff0c;如果每个服务都改动&#xff0c;工作量大&#xff0c;则可以只在网关和用户中心进行改动&#xff0c;也是可以实现服务之间的跳转。 这种方式可以通过在网关服务中生成和验证 Sa-Token&#xff0c;并将其与现有的 Token关联存储在 Redis 中。用户中心…...

鸿蒙开发系统基础能力:【@ohos.hilog (日志打印)】

日志打印 hilog日志系统&#xff0c;使应用/服务可以按照指定级别、标识和格式字符串输出日志内容&#xff0c;帮助开发者了解应用/服务的运行状态&#xff0c;更好地调试程序。 说明&#xff1a; 本模块首批接口从API version 7开始支持。后续版本的新增接口&#xff0c;采用…...

SpringMVC系列十: 中文乱码处理与JSON处理

文章目录 中文乱码处理自定义中文乱码过滤器Spring提供的过滤器处理中文 处理json和HttpMessageConverter<T>处理JSON-ResponseBody处理JSON-RequestBody处理JSON-注意事项和细节HttpMessageConverter<T\>文件下载-ResponseEntity<T\>作业布置 上一讲, 我们学…...

使用MyBatisPlus进行字段的自动填充

使用MyBatisPlus进行字段的自动填充 需求场景 当我们往数据库里面插入一条数据&#xff0c;或者是更新一条数据时&#xff0c;一般都需要标记创建时间create_time和更新时间update_time的值&#xff0c;但是如果我们每张表的每个请求&#xff0c;在执行sql语句的时候我们都手…...

python爬虫之aiohttp多任务异步爬虫

python爬虫之aiohttp多任务异步爬虫 爬取的flash服务如下&#xff1a; from flask import Flask import timeapp Flask(__name__)app.route(/bobo) def index_bobo():time.sleep(2)return Hello boboapp.route(/jay) def index_jay():time.sleep(2)return Hello jayapp.rout…...

1964springboot VUE小程序在线学习管理系统开发mysql数据库uniapp开发java编程计算机网页源码maven项目

一、源码特点 springboot VUE uniapp 小程序 在线学习管理系统是一套完善的完整信息管理类型系统&#xff0c;结合springboot框架uniapp和VUE完成本系统&#xff0c;对理解vue java编程开发语言有帮助系统采用springboot框架&#xff08;MVC模式开发&#xff09;&#xff0c;…...

【前端项目笔记】3 用户管理

用户管理相关功能实现 涉及表单、对话框、Ajax数据请求 基本页面 用户列表开发 在router.js中导入Users.vue 解决用户列表小问题 选中&#xff08;激活&#xff09;子菜单后刷新不显示高亮 给二级菜单绑定单击事件&#xff0c;点击链接时把对应的地址保存到sessionSto…...

【文献及模型、制图分享】基于SSP-RCP不同情景的京津冀地区土地覆被变化模拟

公众号新功能 目前公众号新增以下等功能 1、处理GIS出图、Python制图、区位图、土地利用现状图、土地利用动态度和重心迁移图等等 2、核密度分析、网络od分析、地形分析、空间分析等等 3、地理加权回归、地理探测器、生态环境质量指数、地理加权回归模型影响因素分析、计算…...

基于单片机的智能台灯控制系统

摘要&#xff1a; 文章设计一款单片机智能台灯控制系统&#xff0c;实现对台灯的手动和自动控制功能&#xff0c;以 STC89C52 单片机作为多功能智能台灯的主控制器&#xff0c;光电检测模块检测坐姿&#xff0c;红外传感器检测人体&#xff0c;光敏电阻检测光强&#xff0c;同…...

PrestaShop的一些使用介绍

目录 PrestaShop 是一个功能丰富的开源电子商务解决方案。 1. 以下是其基本概念和架构的一些要点&#xff1a; 2. PrestaShop 的模块开发是扩展其功能的重要方式。以下是对 PrestaShop 模块开发的详细介绍&#xff1a; 开发环境准备&#xff1a; 3. PrestaShop 的模块开发允…...

零基础女生如何入门人工智能,从哪里下手?学习时间大概要多久?

作为一个理工科早期毕业生&#xff0c;出于近乎本能的敏感&#xff0c;格外关注全网热议的ChatGPT。 本来国内就业环境就不好&#xff0c;各行各业内卷越来越严重&#xff0c;加上人工智能的异军突起&#xff0c;各行各业势必将迎来科技进步跨时代的巨大冲击&#xff0c;在此情…...

简答分享python学习进修网站

一、网战推荐 CodeCombat 是一款网页编程游戏。这款编程游戏借鉴了游戏很多设计元素&#xff0c;游戏剧情十分丰富。Codecombat能够学习Python多种语言&#xff0c;这些语言能够运用到游戏设计、网页应用、app的开发上。 Checkio 是一个基于浏览器的游戏&#xff0c;你需要使…...

linux高级编程(I/O)

fputc int fputc(int c, FILE *stream); 功能: 向流中写入一个字符 参数: c:要写入的字符 stream:文件流指针 返回值: 成功返回写入的字符ASCII码值 失败返回EOF fgetc int fgetc(FILE *stream); 功能: 从流中读取一个字符 参数: stream:文件流…...

Java面试——认证与授权

X、常见面试题汇总 1、Shiro与SpringSecutity对比 1&#xff09;Shiro的特点&#xff1a; Shiro 是 Apache 下的项目&#xff0c;相对简单、轻巧&#xff0c;更容易上手使用。 Shiro 权限功能基本都能满足&#xff0c;单点登录都可以实现。且不用与任何的框架或者容器绑定, 可…...

【经典算法OJ题讲解】

1.移除元素 经典算法OJ题1&#xff1a; 移除元素 . - 力扣&#xff08;LeetCode&#xff09;. - 备战技术面试&#xff1f;力扣提供海量技术面试资源&#xff0c;帮助你高效提升编程技能,轻松拿下世界 IT 名企 Dream Offer。https://leetcode.cn/problems/remove-element/desc…...

大数据面试题之Zookeeper面试题

目录 1、介绍下Zookeeper是什么? 2、Zookeeper有什么作用?优缺点?有什么应用场景? 3、Zookeeper的选举策略&#xff0c;leader和follower的区别? 4、介绍下Zookeeper选举算法 5、Zookeeper的节点类型有哪些?分别作用是什么? 6、Zookeeper的节点数怎么设置比较好? …...

JVM 内存区域

一、运行时数据区域 Java 虚拟机在执行 Java 程序的过程中&#xff0c;会把它管理的内存划分成若干个不同的数据区域。 JDK 1.8 和之前的版本略有不同&#xff0c;这里介绍 JDK 1.7 和 JDK 1.8 两个版本。 JDK 1.7&#xff1a; 线程私有&#xff1a; 程序计数器虚拟机栈本地…...

全网最强剖析Spring AOP底层原理

相信各位读者对于Spring AOP的理解都是一知半解&#xff0c;只懂使用&#xff0c;却不懂原理。网上关于Spring AOP的讲解层出不穷&#xff0c;但是易于理解&#xff0c;让人真正掌握原理的文章屈指可数。笔者针对这一痛点需求&#xff0c;决定写一篇关于Spring AOP原理的优质博…...

Vscode中的行尾序列CRLF/LF不兼容问题

最近开发的的时候&#xff0c;打开项目文件经常会出现爆红错误提示信息&#xff0c;显示如下图&#xff1a; 这东西太烦人了&#xff0c;毕竟谁都不希望在遍地都是爆红的代码里写东西&#xff0c;就像能解决这个问题&#xff0c;根据提示可以知道这是vscode中使用的prettier插件…...

常见加密方式:MD5、DES/AES、RSA、Base64

16/32位的数据&#xff0c;最有可能就是使用md5加密的 使用对称加密的时候&#xff0c;双方使用相同的私钥 私钥&#xff1a;单独请求/隐藏在前端的隐藏标签当中 二、RSA非对称密钥加密 公钥加密&#xff0c;私钥解密 私钥是通过公钥计算生成的 加密解密算法都在js源文件当…...

OpenLayers 可视化之热力图

注&#xff1a;当前使用的是 ol 5.3.0 版本&#xff0c;天地图使用的key请到天地图官网申请&#xff0c;并替换为自己的key 热力图&#xff08;Heatmap&#xff09;又叫热点图&#xff0c;是一种通过特殊高亮显示事物密度分布、变化趋势的数据可视化技术。采用颜色的深浅来显示…...

(十)学生端搭建

本次旨在将之前的已完成的部分功能进行拼装到学生端&#xff0c;同时完善学生端的构建。本次工作主要包括&#xff1a; 1.学生端整体界面布局 2.模拟考场与部分个人画像流程的串联 3.整体学生端逻辑 一、学生端 在主界面可以选择自己的用户角色 选择学生则进入学生登录界面…...

《Qt C++ 与 OpenCV:解锁视频播放程序设计的奥秘》

引言:探索视频播放程序设计之旅 在当今数字化时代,多媒体应用已渗透到我们生活的方方面面,从日常的视频娱乐到专业的视频监控、视频会议系统,视频播放程序作为多媒体应用的核心组成部分,扮演着至关重要的角色。无论是在个人电脑、移动设备还是智能电视等平台上,用户都期望…...

JavaScript 中的 ES|QL:利用 Apache Arrow 工具

作者&#xff1a;来自 Elastic Jeffrey Rengifo 学习如何将 ES|QL 与 JavaScript 的 Apache Arrow 客户端工具一起使用。 想获得 Elastic 认证吗&#xff1f;了解下一期 Elasticsearch Engineer 培训的时间吧&#xff01; Elasticsearch 拥有众多新功能&#xff0c;助你为自己…...

Java 8 Stream API 入门到实践详解

一、告别 for 循环&#xff01; 传统痛点&#xff1a; Java 8 之前&#xff0c;集合操作离不开冗长的 for 循环和匿名类。例如&#xff0c;过滤列表中的偶数&#xff1a; List<Integer> list Arrays.asList(1, 2, 3, 4, 5); List<Integer> evens new ArrayList…...

MFC内存泄露

1、泄露代码示例 void X::SetApplicationBtn() {CMFCRibbonApplicationButton* pBtn GetApplicationButton();// 获取 Ribbon Bar 指针// 创建自定义按钮CCustomRibbonAppButton* pCustomButton new CCustomRibbonAppButton();pCustomButton->SetImage(IDB_BITMAP_Jdp26)…...

Opencv中的addweighted函数

一.addweighted函数作用 addweighted&#xff08;&#xff09;是OpenCV库中用于图像处理的函数&#xff0c;主要功能是将两个输入图像&#xff08;尺寸和类型相同&#xff09;按照指定的权重进行加权叠加&#xff08;图像融合&#xff09;&#xff0c;并添加一个标量值&#x…...

【大模型RAG】Docker 一键部署 Milvus 完整攻略

本文概要 Milvus 2.5 Stand-alone 版可通过 Docker 在几分钟内完成安装&#xff1b;只需暴露 19530&#xff08;gRPC&#xff09;与 9091&#xff08;HTTP/WebUI&#xff09;两个端口&#xff0c;即可让本地电脑通过 PyMilvus 或浏览器访问远程 Linux 服务器上的 Milvus。下面…...

新能源汽车智慧充电桩管理方案:新能源充电桩散热问题及消防安全监管方案

随着新能源汽车的快速普及&#xff0c;充电桩作为核心配套设施&#xff0c;其安全性与可靠性备受关注。然而&#xff0c;在高温、高负荷运行环境下&#xff0c;充电桩的散热问题与消防安全隐患日益凸显&#xff0c;成为制约行业发展的关键瓶颈。 如何通过智慧化管理手段优化散…...

解决本地部署 SmolVLM2 大语言模型运行 flash-attn 报错

出现的问题 安装 flash-attn 会一直卡在 build 那一步或者运行报错 解决办法 是因为你安装的 flash-attn 版本没有对应上&#xff0c;所以报错&#xff0c;到 https://github.com/Dao-AILab/flash-attention/releases 下载对应版本&#xff0c;cu、torch、cp 的版本一定要对…...