【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接口获取商品销量、优惠价、商品标题等参数示例
关键词搜索商品接口的作用是提供搜索功能,让用户根据关键词在电商平台上搜索商品,并根据搜索条件和偏好获取相关的商品列表和推荐结果,提高用户购物体验和准确度。对于电商平台而言,这个接口也能帮助用户发现更多商品、提升销量和…...
【外汇天眼】价格波动的节奏感:优化止盈方法!
止盈,依然是一种经验,而不是一种技术。它涉及到价格波动的灵活应对,以确保我们不会错失潜在的盈利,同时也不会让盈利被逆市波动所侵蚀。以下是关于如何有效实施止盈策略的一些建议: 首先,我们要明确&#…...
大话软工笔记—需求分析概述
需求分析,就是要对需求调研收集到的资料信息逐个地进行拆分、研究,从大量的不确定“需求”中确定出哪些需求最终要转换为确定的“功能需求”。 需求分析的作用非常重要,后续设计的依据主要来自于需求分析的成果,包括: 项目的目的…...
树莓派超全系列教程文档--(61)树莓派摄像头高级使用方法
树莓派摄像头高级使用方法 配置通过调谐文件来调整相机行为 使用多个摄像头安装 libcam 和 rpicam-apps依赖关系开发包 文章来源: http://raspberry.dns8844.cn/documentation 原文网址 配置 大多数用例自动工作,无需更改相机配置。但是,一…...
突破不可导策略的训练难题:零阶优化与强化学习的深度嵌合
强化学习(Reinforcement Learning, RL)是工业领域智能控制的重要方法。它的基本原理是将最优控制问题建模为马尔可夫决策过程,然后使用强化学习的Actor-Critic机制(中文译作“知行互动”机制),逐步迭代求解…...
练习(含atoi的模拟实现,自定义类型等练习)
一、结构体大小的计算及位段 (结构体大小计算及位段 详解请看:自定义类型:结构体进阶-CSDN博客) 1.在32位系统环境,编译选项为4字节对齐,那么sizeof(A)和sizeof(B)是多少? #pragma pack(4)st…...
3.3.1_1 检错编码(奇偶校验码)
从这节课开始,我们会探讨数据链路层的差错控制功能,差错控制功能的主要目标是要发现并且解决一个帧内部的位错误,我们需要使用特殊的编码技术去发现帧内部的位错误,当我们发现位错误之后,通常来说有两种解决方案。第一…...
【HarmonyOS 5.0】DevEco Testing:鸿蒙应用质量保障的终极武器
——全方位测试解决方案与代码实战 一、工具定位与核心能力 DevEco Testing是HarmonyOS官方推出的一体化测试平台,覆盖应用全生命周期测试需求,主要提供五大核心能力: 测试类型检测目标关键指标功能体验基…...
SCAU期末笔记 - 数据分析与数据挖掘题库解析
这门怎么题库答案不全啊日 来简单学一下子来 一、选择题(可多选) 将原始数据进行集成、变换、维度规约、数值规约是在以下哪个步骤的任务?(C) A. 频繁模式挖掘 B.分类和预测 C.数据预处理 D.数据流挖掘 A. 频繁模式挖掘:专注于发现数据中…...
Python爬虫实战:研究feedparser库相关技术
1. 引言 1.1 研究背景与意义 在当今信息爆炸的时代,互联网上存在着海量的信息资源。RSS(Really Simple Syndication)作为一种标准化的信息聚合技术,被广泛用于网站内容的发布和订阅。通过 RSS,用户可以方便地获取网站更新的内容,而无需频繁访问各个网站。 然而,互联网…...
多种风格导航菜单 HTML 实现(附源码)
下面我将为您展示 6 种不同风格的导航菜单实现,每种都包含完整 HTML、CSS 和 JavaScript 代码。 1. 简约水平导航栏 <!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><meta name"viewport&qu…...
人机融合智能 | “人智交互”跨学科新领域
本文系统地提出基于“以人为中心AI(HCAI)”理念的人-人工智能交互(人智交互)这一跨学科新领域及框架,定义人智交互领域的理念、基本理论和关键问题、方法、开发流程和参与团队等,阐述提出人智交互新领域的意义。然后,提出人智交互研究的三种新范式取向以及它们的意义。最后,总结…...
