ruoyi登录功能源码分析
Ruoyi登录功能源码分析
上一篇文章我们分析了一下若依登录验证码生成的代码,今天我们来分析一下登录功能的代码
1、发送登录请求
前端通过http://localhost/dev-api/login向后端发送登录请求并携带用户的登录表单
在后端中的com.ruoyi.web.controller.system包下的SysLoginController响应登录信息,后端接受到请求后首先将操作信息设置为成功,然后对用户的登录表单进行校验,校验通过返回token
2、登录校验
接下来我们来看一看登录校验的代码
/*** 校验验证码* * @param username 用户名* @param code 验证码* @param uuid 唯一标识* @return 结果*/public void validateCaptcha(String username, String code, String uuid){// 判断当前是否需要生成验证码boolean captchaEnabled = configService.selectCaptchaEnabled();if (captchaEnabled){// 根据生成验证码时返回给前端的uuid拼接验证码在redis中的key值String verifyKey = CacheConstants.CAPTCHA_CODE_KEY + StringUtils.nvl(uuid, "");// 从redis中获取验证码String captcha = redisCache.getCacheObject(verifyKey);// 删除验证码redisCache.deleteObject(verifyKey);if (captcha == null){// 开启异步任务记录用户信息AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.expire")));throw new CaptchaExpireException();}if (!code.equalsIgnoreCase(captcha)){AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.error")));throw new CaptchaException();}}}
2.1 前置校验
后端在进行登录校验时首先会对登录验证码进行校验然后会对用户名和密码进行前置校验,验证码校验上一篇文章中已经说过所以这一章不再赘述
登录前置校验会调用loginPreCheck函数
// 验证码校验validateCaptcha(username, code, uuid);// 登录前置校验loginPreCheck(username, password);
/*** 校验验证码* * @param username 用户名* @param code 验证码* @param uuid 唯一标识* @return 结果*/public void validateCaptcha(String username, String code, String uuid){// 判断当前是否需要生成验证码boolean captchaEnabled = configService.selectCaptchaEnabled();if (captchaEnabled){// 根据生成验证码时返回给前端的uuid拼接验证码在redis中的key值String verifyKey = CacheConstants.CAPTCHA_CODE_KEY + StringUtils.nvl(uuid, "");// 从redis中获取验证码String captcha = redisCache.getCacheObject(verifyKey);// 删除验证码redisCache.deleteObject(verifyKey);if (captcha == null){// 开启异步任务记录用户信息AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.expire")));throw new CaptchaExpireException();}if (!code.equalsIgnoreCase(captcha)){AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.error")));throw new CaptchaException();}}}
2.2、登录认证
接下来是生成token,首先定义一个UsernamePasswordAuthenticationToken对象用来存放用户登录提供的认证凭证表示一个为认证的请求,然后是将用户的提供的登录认证凭证放到线程池的上下文中,调用authenticationManager的authenticate方法进行认证,最后记录登录信息并返回token
/*** 登录验证* * @param username 用户名* @param password 密码* @param code 验证码* @param uuid 唯一标识* @return 结果*/public String login(String username, String password, String code, String uuid){// 验证码校验validateCaptcha(username, code, uuid);// 登录前置校验loginPreCheck(username, password);// 用户验证Authentication authentication = null;try{// 将用户名和密码存放到UsernamePasswordAuthenticationToken中UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(username, password);// 将authenticationToken放到线程池的上下文中AuthenticationContextHolder.setContext(authenticationToken);// 该方法会去调用UserDetailsServiceImpl.loadUserByUsername// 对用户信息进行认证authentication = authenticationManager.authenticate(authenticationToken);}catch (Exception e){if (e instanceof BadCredentialsException){// 认证失败记录错误信息AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match")));throw new UserPasswordNotMatchException();}else{AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, e.getMessage()));throw new ServiceException(e.getMessage());}}finally{// 清除上下文中的信息AuthenticationContextHolder.clearContext();}// 认证成功 记录信息AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success")));// 从认证成功的authentication中取出用户信息LoginUser loginUser = (LoginUser) authentication.getPrincipal();// 记录登录信息recordLoginInfo(loginUser.getUserId());// 生成tokenreturn tokenService.createToken(loginUser);}
通过SpringSecurity的过滤器链调用UserDetailsServiceImpl进行用户名和密码的校验
/*** 用户验证处理** @author ruoyi*/
@Service
public class UserDetailsServiceImpl implements UserDetailsService
{private static final Logger log = LoggerFactory.getLogger(UserDetailsServiceImpl.class);@Autowiredprivate ISysUserService userService;@Autowiredprivate SysPasswordService passwordService;@Autowiredprivate SysPermissionService permissionService;@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException{// 根据用户名查询用户SysUser user = userService.selectUserByUserName(username);if (StringUtils.isNull(user)){// 用户为空log.info("登录用户:{} 不存在.", username);throw new ServiceException(MessageUtils.message("user.not.exists"));}else if (UserStatus.DELETED.getCode().equals(user.getDelFlag())){log.info("登录用户:{} 已被删除.", username);throw new ServiceException(MessageUtils.message("user.password.delete"));}else if (UserStatus.DISABLE.getCode().equals(user.getStatus())){log.info("登录用户:{} 已被停用.", username);throw new ServiceException(MessageUtils.message("user.blocked"));}// 校验密码passwordService.validate(user);return createLoginUser(user);}public UserDetails createLoginUser(SysUser user){return new LoginUser(user.getUserId(), user.getDeptId(), user, permissionService.getMenuPermission(user));}
}
2.3 生成token令牌
生成token
/*** 创建令牌** @param loginUser 用户信息* @return 令牌*/public String createToken(LoginUser loginUser){String token = IdUtils.fastUUID();loginUser.setToken(token);setUserAgent(loginUser);refreshToken(loginUser);Map<String, Object> claims = new HashMap<>();claims.put(Constants.LOGIN_USER_KEY, token);return createToken(claims);}
设置用户信息
/*** 设置用户代理信息** @param loginUser 登录信息*/public void setUserAgent(LoginUser loginUser){UserAgent userAgent = UserAgent.parseUserAgentString(ServletUtils.getRequest().getHeader("User-Agent"));String ip = IpUtils.getIpAddr();loginUser.setIpaddr(ip);loginUser.setLoginLocation(AddressUtils.getRealAddressByIP(ip));loginUser.setBrowser(userAgent.getBrowser().getName());loginUser.setOs(userAgent.getOperatingSystem().getName());}
将token缓存到redis并设置过期时间(默认30分钟)
/*** 刷新令牌有效期** @param loginUser 登录信息*/public void refreshToken(LoginUser loginUser){loginUser.setLoginTime(System.currentTimeMillis());loginUser.setExpireTime(loginUser.getLoginTime() + expireTime * MILLIS_MINUTE);// 根据uuid将loginUser缓存String userKey = getTokenKey(loginUser.getToken());redisCache.setCacheObject(userKey, loginUser, expireTime, TimeUnit.MINUTES);}
-
expireTime * MILLIS_MINUTE);
// 根据uuid将loginUser缓存
String userKey = getTokenKey(loginUser.getToken());
redisCache.setCacheObject(userKey, loginUser, expireTime, TimeUnit.MINUTES);}
到就登录成功了并且后端成功返回token
相关文章:

ruoyi登录功能源码分析
Ruoyi登录功能源码分析 上一篇文章我们分析了一下若依登录验证码生成的代码,今天我们来分析一下登录功能的代码 1、发送登录请求 前端通过http://localhost/dev-api/login向后端发送登录请求并携带用户的登录表单 在后端中的com.ruoyi.web.controller.system包下…...

推动产业数字化转型,六个方面引领变革
从工业经济时代走向数字经济时代,世界经济发生着全方位、革命性的变化,产业数字化便是最显著的表现之一。当前,产业数字化不断深入发展,平台经济、工业互联网、智能制造等新业态、新模式不断涌现,成为了数字经济的重要…...

设施布置之车间布局优化SLP分析
一 物流分析(Flow Analysis) 的基本方法 1、当物料移动是工艺过程的主要部分时,物流分析就是工厂布置设计的核心工作,也是物料搬运分析的开始。 2、零部件物流是该部件在工厂内移动时所走过的路线, 物流分析不仅要考虑…...

帕金森的锻炼方式
帕金森病,这个看似陌生的名词,其实离我们并不遥远。它是一种常见的神经系统疾病,影响着许多中老年人的生活质量。虽然帕金森病目前尚无根治之法,但通过科学合理的日常锻炼,可以有效缓解病情,提高生活质量。…...
PyTorch(一)模型训练过程
PyTorch(一)模型训练过程 #c 总结 实践总结 该实践从「数据处理」开始到最后利用训练好的「模型」预测,感受到了整个模型的训练过程。其中也有部分知识点,例如定义神经网络,只是初步的模仿,有一个比较浅的…...

windows下cmd命令行模式中cd变换路径命令无效的解决办法
一,出现的情况 二,解决方法 当出现转换盘的时候打开 cmd 之后可能是无法生效的 ,因为在cmd 中转换盘首先需要用到换盘符 。 Solve1 : 先进行换盘 C: c: // 转换到 C盘 D: d: // 转化到 D盘 Solve2 : 直接进行强转 cd /dE:\ACM算法资源\XCP…...

收藏||电商数据采集流程||电商数据采集API接口
商务数据分析的流程 第一步:明确分析目的。首先要明确分析目的,并把分析目的分解成若干个不同的分析要点,然后梳理分析思路,最后搭建分析框架。 第二步:数据采集。主流电商API接口数据采集,一般可以通过数…...

修改源码,打patch包,线上环境不生效
1.首先看修改的源码文件是否正确 在node_modules中,找对应的包,然后查看包中package.json 的main和module。如果用require引入,则修改lib下面的组件,如果是import引入则修改es下面的文件 main 对应commonjs引入方式的程序入口文件…...

NUC980-OLED实现全中文字库的方法
1.背景 有一个产品,客户需要屏幕展示一些内容,要带一些中文,实现了OLED12864的驱动,但是它不带字库,现在要实现OLED全字库的显示 2.制作原始字库 下载软件pctolcd2002 设置 制作字库 打开原始文件 用软件自带的&…...
UEFI 启动原理及qemu 虚拟化中使用
UEFI 启动原理及qemu 虚拟化中使用 什么是BIOS?什么是 UEFI? 什么是BIOS? 计算机启动时会加载 BIOS,以初始化和测试硬件功能。它使用 POST 或 Power On Self Test 来确保硬件配置有效且工作正常,然后寻找存储引导设…...

35、正则表达式
一、正则表达式命令 正则表达式:匹配的是文本内容,linux的文本三剑客都是针对文本内容。 grep 过滤文本内容 sed 针对文本内容进行增删改查 awk 按行取列 文本三剑客----都是按照行进行匹配。 1.1、grep筛选: grep的作用就是…...

Ubuntu20.04中复现FoundationPose
Ubuntu20.04中复现FoundationPose 文章目录 Ubuntu20.04中复现FoundationPose1.安装cuda和cudnn2.下载相关资源3.环境配置4.运行model-based demo5.运行ycbv demoReference 🚀 非常重要的环境配置 🚀 ubuntu 20.04cuda 11.8.0cudnn v8.9.7python 3.9.19…...
【Qt快速入门(四)】- QLabel文本框的使用
目录 Qt快速入门(四)- QLabel文本框的使用QLabel文本框的使用QLabel的基本用法1. 创建和设置文本2. 动态设置文本 设置文本样式1.设置字体和颜色2.文本对齐方式3.富文本显示 显示图片QLabel的交互功能可点击标签 QLabel的高级特性1.缩放图片以适应标签大…...

用Python设置Excel工作表网格线的隐藏与显示
Excel表格界面的直观性很大程度上得益于表格中的网格线设计,这些线条帮助用户精确对齐数据,清晰划分单元格。网格线是Excel界面中默认显示的辅助线,用于辅助定位,与单元格边框不痛,不影响打印输出。然而,在…...
自回归模型胜过扩散模型:用于可扩展图像生成的 Llama
📜 文献卡 Autoregressive Model Beats Diffusion: Llama for Scalable Image Generation作者: Peize Sun; Yi Jiang; Shoufa Chen; Shilong Zhang; Bingyue Peng; Ping Luo; Zehuan YuanDOI: 10.48550/arXiv.2406.06525摘要: We introduce LlamaGen, a new family …...

访问外网的安全保障——反向沙箱
反向沙箱作为一种网络安全技术,其核心理念在于通过构建一个隔离且受控的环境,来有效阻止潜在的网络威胁对真实系统的影响。在当今日益复杂的网络环境中,如何借助反向沙箱实现安全上网,已成为众多用户关注的焦点。 随着信息化的发…...
【绝对有用】C++ 字符串进行排序、vector增加内容 和 剔除值
在 C 中对字符串进行排序,可以使用标准库中的 std::sort 函数。std::sort 函数可以用于容器或范围内的元素排序,包括字符串中的字符。以下是一个简单的示例代码,展示了如何对字符串中的字符进行排序: #include <iostream> …...

GenICam标准(一)
系列文章目录 GenICam标准(一) GenICam标准(二) GenICam标准(三) GenICam标准(四) GenICam标准(五) GenICam标准(六) 文章目录 系列文…...

【Redis】分布式锁基本理论与简单实现
目录 分布式锁解释作用特性实现方式MySQL、Redis、Zookeeper三种方式对比 原理 reids分布式锁原理目的容错redis简单分布式锁实现锁接口实现类下单场景的实现容错场景1解决思路优化代码 容错场景2Lua脚本Redis利用Lua脚本解决多条命令原子性问题 释放锁的业务流程Lua脚本来表示…...

Web开发技术大作业(HTML\CSS\PHP\MYSQL\JS)
从6月13日到6月15日,经过一系列的操作,终于把老师布置的大作业写完了,虽然有很多水分,很多东西都是为了应付(特别是最后做的那几个网页),真的是惨不忍睹,不过既然花时间写了…...
conda相比python好处
Conda 作为 Python 的环境和包管理工具,相比原生 Python 生态(如 pip 虚拟环境)有许多独特优势,尤其在多项目管理、依赖处理和跨平台兼容性等方面表现更优。以下是 Conda 的核心好处: 一、一站式环境管理:…...

从零开始打造 OpenSTLinux 6.6 Yocto 系统(基于STM32CubeMX)(九)
设备树移植 和uboot设备树修改的内容同步到kernel将设备树stm32mp157d-stm32mp157daa1-mx.dts复制到内核源码目录下 源码修改及编译 修改arch/arm/boot/dts/st/Makefile,新增设备树编译 stm32mp157f-ev1-m4-examples.dtb \stm32mp157d-stm32mp157daa1-mx.dtb修改…...

Android15默认授权浮窗权限
我们经常有那种需求,客户需要定制的apk集成在ROM中,并且默认授予其【显示在其他应用的上层】权限,也就是我们常说的浮窗权限,那么我们就可以通过以下方法在wms、ams等系统服务的systemReady()方法中调用即可实现预置应用默认授权浮…...

IoT/HCIP实验-3/LiteOS操作系统内核实验(任务、内存、信号量、CMSIS..)
文章目录 概述HelloWorld 工程C/C配置编译器主配置Makefile脚本烧录器主配置运行结果程序调用栈 任务管理实验实验结果osal 系统适配层osal_task_create 其他实验实验源码内存管理实验互斥锁实验信号量实验 CMISIS接口实验还是得JlINKCMSIS 简介LiteOS->CMSIS任务间消息交互…...

协议转换利器,profinet转ethercat网关的两大派系,各有千秋
随着工业以太网的发展,其高效、便捷、协议开放、易于冗余等诸多优点,被越来越多的工业现场所采用。西门子SIMATIC S7-1200/1500系列PLC集成有Profinet接口,具有实时性、开放性,使用TCP/IP和IT标准,符合基于工业以太网的…...

Linux中《基础IO》详细介绍
目录 理解"文件"狭义理解广义理解文件操作的归类认知系统角度文件类别 回顾C文件接口打开文件写文件读文件稍作修改,实现简单cat命令 输出信息到显示器,你有哪些方法stdin & stdout & stderr打开文件的方式 系统⽂件I/O⼀种传递标志位…...

云安全与网络安全:核心区别与协同作用解析
在数字化转型的浪潮中,云安全与网络安全作为信息安全的两大支柱,常被混淆但本质不同。本文将从概念、责任分工、技术手段、威胁类型等维度深入解析两者的差异,并探讨它们的协同作用。 一、核心区别 定义与范围 网络安全:聚焦于保…...

sshd代码修改banner
sshd服务连接之后会收到字符串: SSH-2.0-OpenSSH_9.5 容易被hacker识别此服务为sshd服务。 是否可以通过修改此banner达到让人无法识别此服务的目的呢? 不能。因为这是写的SSH的协议中的。 也就是协议规定了banner必须这么写。 SSH- 开头,…...
CppCon 2015 学习:Time Programming Fundamentals
Civil Time 公历时间 特点: 共 6 个字段: Year(年)Month(月)Day(日)Hour(小时)Minute(分钟)Second(秒) 表示…...
node.js的初步学习
那什么是node.js呢? 和JavaScript又是什么关系呢? node.js 提供了 JavaScript的运行环境。当JavaScript作为后端开发语言来说, 需要在node.js的环境上进行当JavaScript作为前端开发语言来说,需要在浏览器的环境上进行 Node.js 可…...