【RuoYi-Cloud项目研究】【ruoyi-auth模块】登录请求(/login)分析
文章目录
- 0. 网关如何处理登录请求
- 1. Controller
- 1.1. 获取用户信息
- 1.2. 创建用户的token
- 2. Service
- 2.1. FeignClient远程查询用户信息
- 2.2. 验证密码
- 3. 何时刷新 token,如何刷新【本文重点】
本文主要是分析登录请求 /login 的过程。
调用过程是:ruoyi-auth —> ruoyi-system
0. 网关如何处理登录请求
登录请求 127.0.0.1:8080/auth/login
是特殊的请求。经过的过滤器有:
AuthFilter(order=-200)
XssFilter(order=-100)
CacheRequestFilter(order=1)
ValidateCodeFilter(order=2)
StripPrefix(order=3)
AuthFilter 认证过滤器对需要排除的 uri 地址,不检查是否有 token 直接放行(需要排除的配置可以在nacos中配置)。默认配置如下:
# 安全配置
security:# 验证码captcha:enabled: truetype: math# 防止XSS攻击xss:enabled: trueexcludeUrls:- /system/notice# 不校验白名单ignore:whites:- /auth/logout- /auth/login- /auth/register- /*/v2/api-docs- /csrf
(注意是“认证”不是“鉴权”,认证主要是判断 token 是否有效,不涉及权限)
1. Controller
@PostMapping("login")public R<?> login(@RequestBody LoginBody form){// 用户登录LoginUser userInfo = sysLoginService.login(form.getUsername(), form.getPassword());// 获取登录tokenreturn R.ok(tokenService.createToken(userInfo));}
- 用户登录
- 生成 token 返回
1.1. 获取用户信息
请看 Service
1.2. 创建用户的token
- 创建token返回给前端
/*** 创建令牌*/
public Map<String, Object> createToken(LoginUser loginUser)
{String token = IdUtils.fastUUID();// <1> 封装用户信息Long userId = loginUser.getSysUser().getUserId();String userName = loginUser.getSysUser().getUserName();loginUser.setToken(token);loginUser.setUserid(userId);loginUser.setUsername(userName);loginUser.setIpaddr(IpUtils.getIpAddr());// <2> 刷新tokenrefreshToken(loginUser);// <3> Jwt存储信息Map<String, Object> claimsMap = new HashMap<String, Object>();claimsMap.put(SecurityConstants.USER_KEY, token);claimsMap.put(SecurityConstants.DETAILS_USER_ID, userId);claimsMap.put(SecurityConstants.DETAILS_USERNAME, userName);// 接口返回信息Map<String, Object> rspMap = new HashMap<String, Object>();rspMap.put("access_token", JwtUtils.createToken(claimsMap));rspMap.put("expires_in", expireTime);return rspMap;
}
在 <1>
处:
把从 ruoyi-system 模块获取到的用户信息(用户、角色、权限)连同关键部分(uuid、userId、userName)重新封装
在 <2>
处:
refreshToken方法负责刷新token。何时刷新、如何刷新是一个值得讨论的问题。在本文的第三章重点讨论了这个问题。
在 <3>
处:
创建 token 的负载信息,把 uuid、userId、userName 作为 token 的负载,来创建 token。并返回给用户
2. Service
public LoginUser login(String username, String password){...// IP黑名单校验【在哪里初始化黑名单的???】String blackStr = Convert.toStr(redisService.getCacheObject(CacheConstants.SYS_LOGIN_BLACKIPLIST));if (IpUtils.isMatchedIp(blackStr, IpUtils.getIpAddr())){recordLogService.recordLogininfor(username, Constants.LOGIN_FAIL, "很遗憾,访问IP已被列入系统黑名单");throw new ServiceException("很遗憾,访问IP已被列入系统黑名单");}// 查询用户信息R<LoginUser> userResult = remoteUserService.getUserInfo(username, SecurityConstants.INNER);...passwordService.validate(user, password);return userInfo;}
主要包括 2 个部分。
- 用 FeignCilent 从远程服务查询用户信息
- 密码验证
注意: remoteUserService.getUserInfo(username, SecurityConstants.INNER)
的 INNER 指定了该请求是“内部”请求,模块 auth 还会对 INNER 做处理。
2.1. FeignClient远程查询用户信息
以下代码com.ruoyi.system.api.RemoteUserService在 ruoyi-api-system
中,只是定义了相关的 api 并不是实现。RuoYi 抽象了与系统相关的 ruoyi-api-system
。在里面写公共的类或接口
@FeignClient(contextId = "remoteUserService", value = ServiceNameConstants.SYSTEM_SERVICE, fallbackFactory = RemoteUserFallbackFactory.class)
public interface RemoteUserService
{/*** 通过用户名查询用户信息** @param username 用户名* @param source 请求来源* @return 结果*/@GetMapping("/user/info/{username}")public R<LoginUser> getUserInfo(@PathVariable("username") String username, @RequestHeader(SecurityConstants.FROM_SOURCE) String source);
}
url是:/user/info/{username}。
调用服务是: ruoyi-system。
设置了降级 fallback 处理:RemoteUserFallbackFactory
备注:获取用户信息的详细分析参考:https://www.yuque.com/yuchangyuan/tkb5br/vktircc4smqw8ggs
2.2. 验证密码
- 验证密码
主要是验证“可重试次数”和“密码正确性”
1、验证重试次数(默认如果错误超过5次就锁定 10分钟)
2、验证密码是否与数据库匹配。如登录成功需要清除密码错误次数。注册和验证密码用到的密码验证器要一致。
3. 何时刷新 token,如何刷新【本文重点】
- 何时刷新
只有在将要达到过期时间,才会刷新,来续期,通常可以认为是 2/3 的时间点。
① 调用refresh方法刷新:refresh 接口
② 创建 token 时:createToken方法
③ 设置用户信息时:setLoginUser方法
④ 验证 token 时:verifyToken方法
主要是第四点。是通过 mvc 拦截器实现的
- 如何刷新
1、定时任务方案:
① 拦截用户请求,保存 key=userId,value=需要刷新的时间点到一个全局的 map 中
② 用 quartz 启动一个定时任务间隔一段时间扫描 map 来刷新
缺点:不太好控制
2、拦截器方案:
原理:是通过 mvc 的拦截器 HandlerInterceptor 实现的。
为什么不通过过滤器实现,而是拦截器?
答案:因为我们的目的不是要过滤掉请求,而是拦截请求并根据条件设置token的过期时间
public class HeaderInterceptor implements AsyncHandlerInterceptor
{@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception{........String token = SecurityUtils.getToken();if (StringUtils.isNotEmpty(token)){LoginUser loginUser = AuthUtil.getLoginUser(token);if (StringUtils.isNotNull(loginUser)){// 验证和刷新 token 的过期时间AuthUtil.verifyLoginUserExpire(loginUser);SecurityContextHolder.set(SecurityConstants.LOGIN_USER, loginUser);}}return true;}
}
如上面的代码,RuoYi 采用的也是拦截器方案,在HeaderInterceptor拦截器中,针对每个请求验证和刷新 token 的过期时间。详情参考:https://www.yuque.com/yuchangyuan/tkb5br/d387fbc5d3f5522fa013b5e087a0dad9
相关文章:
【RuoYi-Cloud项目研究】【ruoyi-auth模块】登录请求(/login)分析
文章目录 0. 网关如何处理登录请求1. Controller1.1. 获取用户信息1.2. 创建用户的token 2. Service2.1. FeignClient远程查询用户信息2.2. 验证密码 3. 何时刷新 token,如何刷新【本文重点】 本文主要是分析登录请求 /login 的过程。 调用过程是:ruoyi-…...

Git 学习笔记 | Git 项目创建及克隆
Git 学习笔记 | Git 项目创建及克隆 Git 学习笔记 | Git 项目创建及克隆创建工作目录与常用指令本地仓库搭建克隆远程仓库 Git 学习笔记 | Git 项目创建及克隆 创建工作目录与常用指令 工作目录(WorkSpace)一般就是你希望Git帮助你管理的文件夹,可以是…...

C++默认参数(实参)
在本文中,您将学习什么是默认参数,如何使用它们以及使用它的必要声明。在C 编程中,您可以提供函数参数的默认值。默认参数背后的想法很简单。如果通过传递参数调用函数,则这些参数将由函数使用。但是,如果在调用函数时…...
Datax数据同步支持SqlServer 主键自增
允许写入的SQL SET IDENTITY_INSERT table_name ON;-- 插入数据,指定主键值 INSERT INTO table_name (id, column1, column2, ...) VALUES (new_id_value, value1, value2, ...);SET IDENTITY_INSERT table_name OFF; 写入插件处理 核心类:com.alibab…...
C++开发学习笔记3
C 中枚举的使用 在C中,枚举常量(Enumeration Constants)是一种定义命名常量的方式。枚举类型允许我们为一组相关的常量赋予有意义的名称,并将它们作为一个独立的类型来使用。 以下是定义和使用枚举常量的示例: enum…...
计算机中常说的SDK是什么意思?
SDK是Software Development Kit的英文缩写,意思是软件开发包。 软件开发包中往往包含有多种辅助进行软件开发的内容,包括一些软件开发工具、文档说明、库和示例代码。这些内容能够帮助使用SDK进行软件开发的人员更好地开发程序。 SDK的作用就是简化软件…...

漏刻有时数据可视化大屏(16)数据指标KPI和柱图折线图混排
CSS样式表 /*面板*/ .pannel {width: 100%;margin-top: 30px;clear: both; }.item_l {float: left;width: 20%; /*3格60%*/margin: 0; }.item_r {float: left;width: 10%; /*4格40%*/margin: 0; }.item_child {float: left;width: 50%; }.item_child_b {float: left;width: 10…...

基于Stable Diffusion的图像合成数据集
当前从文本输入生成合成图像的模型不仅能够生成非常逼真的照片,而且还能够处理大量不同的对象。 在论文“评估使用稳定扩散生成的合成图像数据集”中,我们使用“稳定扩散”模型来研究哪些对象和类型表现得如此逼真,以便后续图像分类正确地分配…...
云计算:常用运维软件工具
目录 一、理论 1.云管理工具 2.虚拟化工具 3.容器管理工具 4.运维自动化工具 5.版本控制工具 6.配置管理工具 7.编辑器工具 8.代码质量工具 9.网络管理工具 10.数据库管理工具 11.数据中心设备管理工具 12.数据可视化工具 13.服务器管理工具 14.应用性能管理工具…...

多测师肖sir_高级金牌讲师_python的安装002
一、python安装 1、python包(我们目前学习的版本是3.7) python-3.7.3 版本 2、Python下载的官网:https://www.python.org/downloads/ 最新包:3.12 3、下载好python安装包,在新建一个python文件件,我们要…...
gin实现event stream
event stream是属于http的一种通信方式,可以实现服务器主动推送。原理于客户端请求服务器之后一直保持链接,服务端持续返回结果给客户端。相比较于websocket有如下区别: 基于http的通信方式,在各类框架的加持下不需要开发人员自己…...
pytorch中transform库中常用的函数有哪些及其用法?
在PyTorch的torchvision.transforms库中,有许多常用的图像变换函数可用于数据增强和预处理。下面列举了一些常用的函数及其用法: Resize(size): 调整图像大小为给定的尺寸。 transform transforms.Resize((256, 256))RandomCrop(size, paddingNone): 随…...

抖音手机实景无人直播间怎么搭建?
手机无人直播已成为用户直播和商家直播带货的一项热门技术趋势,为消费者提供了全新的观看体验。无人直播,顾名思义,即通过无人直播软件或数字人来进行无人直播。这一技术的广泛应用,不仅为短视频渠道带来了更丰富的玩法࿰…...

【新书推荐】当 Python 遇到 ChatGPT —— 自动化办公落地
文章目录 当 Python 遇到 ChatGPT:一种强大的组合1. 文本生成2. 自动翻译3. 对话生成4. 情感分析 新书推荐《Python自动化办公应用大全(ChatGPT版):从零开始教编程小白一键搞定烦琐工作(上下册)》前言内容简…...

RSA攻击:Smooth攻击
目录 前言:缘起 P-1光滑攻击 P1光滑攻击 前缀知识 Lucas-Subsquence(卢卡斯序列) 编码实现与理解 小试牛刀 [NCTF 2019]childRSA 引用 前言:缘起 Smooth攻击(光滑攻击),在最近刷题的时候总是能偶尔蹦跶到我的脑子里面。不是天天遇见它&am…...
什么是位域和位段?如何定义和使用位域?
位域(Bit Fields)是C语言中一种用于在数据结构中以位为单位对数据进行精确控制的技术。它们允许程序员将一个整数字段分割成多个更小的部分,每个部分可以存储不同的信息。位域通常在对内存节省要求高、数据压缩或硬件寄存器描述等情况下使用。…...
网络攻防备课笔记
从“踩点”到“创建后门”的攻击流程 踩点:攻击者在实施攻击前对目标进行初步的探索和调查的过程,包括收集目标的IP地址、开放的端口、服务版本、可能的漏洞等信息。 扫描:使用工具如Nmap、Masscan等对目标进行端口扫描,找出开放…...

Apache Solr9.3 快速上手
Apache Solr 简介 Solr是Apache的顶级开源项目,使用java开发 ,基于Lucene的全文检索服务器。 Solr比Lucene提供了更多的查询语句,而且它可扩展、可配置,同时它对Lucene的性能进行了优化。 安装 下载 : 下载地址解压 : tar -zxv…...
按关键字搜索淘宝商品API接口获取商品销量、优惠价、商品标题等参数示例
关键词搜索商品接口的作用是提供搜索功能,让用户根据关键词在电商平台上搜索商品,并根据搜索条件和偏好获取相关的商品列表和推荐结果,提高用户购物体验和准确度。对于电商平台而言,这个接口也能帮助用户发现更多商品、提升销量和…...

【外汇天眼】价格波动的节奏感:优化止盈方法!
止盈,依然是一种经验,而不是一种技术。它涉及到价格波动的灵活应对,以确保我们不会错失潜在的盈利,同时也不会让盈利被逆市波动所侵蚀。以下是关于如何有效实施止盈策略的一些建议: 首先,我们要明确&#…...

(二)TensorRT-LLM | 模型导出(v0.20.0rc3)
0. 概述 上一节 对安装和使用有个基本介绍。根据这个 issue 的描述,后续 TensorRT-LLM 团队可能更专注于更新和维护 pytorch backend。但 tensorrt backend 作为先前一直开发的工作,其中包含了大量可以学习的地方。本文主要看看它导出模型的部分&#x…...
第25节 Node.js 断言测试
Node.js的assert模块主要用于编写程序的单元测试时使用,通过断言可以提早发现和排查出错误。 稳定性: 5 - 锁定 这个模块可用于应用的单元测试,通过 require(assert) 可以使用这个模块。 assert.fail(actual, expected, message, operator) 使用参数…...

DIY|Mac 搭建 ESP-IDF 开发环境及编译小智 AI
前一阵子在百度 AI 开发者大会上,看到基于小智 AI DIY 玩具的演示,感觉有点意思,想着自己也来试试。 如果只是想烧录现成的固件,乐鑫官方除了提供了 Windows 版本的 Flash 下载工具 之外,还提供了基于网页版的 ESP LA…...

C# 类和继承(抽象类)
抽象类 抽象类是指设计为被继承的类。抽象类只能被用作其他类的基类。 不能创建抽象类的实例。抽象类使用abstract修饰符声明。 抽象类可以包含抽象成员或普通的非抽象成员。抽象类的成员可以是抽象成员和普通带 实现的成员的任意组合。抽象类自己可以派生自另一个抽象类。例…...
三体问题详解
从物理学角度,三体问题之所以不稳定,是因为三个天体在万有引力作用下相互作用,形成一个非线性耦合系统。我们可以从牛顿经典力学出发,列出具体的运动方程,并说明为何这个系统本质上是混沌的,无法得到一般解…...
Mobile ALOHA全身模仿学习
一、题目 Mobile ALOHA:通过低成本全身远程操作学习双手移动操作 传统模仿学习(Imitation Learning)缺点:聚焦与桌面操作,缺乏通用任务所需的移动性和灵活性 本论文优点:(1)在ALOHA…...
使用Matplotlib创建炫酷的3D散点图:数据可视化的新维度
文章目录 基础实现代码代码解析进阶技巧1. 自定义点的大小和颜色2. 添加图例和样式美化3. 真实数据应用示例实用技巧与注意事项完整示例(带样式)应用场景在数据科学和可视化领域,三维图形能为我们提供更丰富的数据洞察。本文将手把手教你如何使用Python的Matplotlib库创建引…...
React---day11
14.4 react-redux第三方库 提供connect、thunk之类的函数 以获取一个banner数据为例子 store: 我们在使用异步的时候理应是要使用中间件的,但是configureStore 已经自动集成了 redux-thunk,注意action里面要返回函数 import { configureS…...

Chrome 浏览器前端与客户端双向通信实战
Chrome 前端(即页面 JS / Web UI)与客户端(C 后端)的交互机制,是 Chromium 架构中非常核心的一环。下面我将按常见场景,从通道、流程、技术栈几个角度做一套完整的分析,特别适合你这种在分析和改…...
Java求职者面试指南:Spring、Spring Boot、Spring MVC与MyBatis技术解析
Java求职者面试指南:Spring、Spring Boot、Spring MVC与MyBatis技术解析 一、第一轮基础概念问题 1. Spring框架的核心容器是什么?它的作用是什么? Spring框架的核心容器是IoC(控制反转)容器。它的主要作用是管理对…...