jeecg库login登录过程分析笔记

news2024/11/17 10:48:25

jeecg库(版本jeecg-boot-v3.5.1last)实现了用户登录功能,二开时为了借鉴jeecg用户登录的方法,跑了一遍登录方法:

org.jeecg.modules.system.controller.LoginController#login

定义这个方法的类的路径是:

jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/system/controller/LoginController.java

截图:

login方法代码:

	@ApiOperation("登录接口")
	@RequestMapping(value = "/login", method = RequestMethod.POST)
	public Result<JSONObject> login(@RequestBody SysLoginModel sysLoginModel){
		Result<JSONObject> result = new Result<JSONObject>();
		String username = sysLoginModel.getUsername();
		String password = sysLoginModel.getPassword();
		//update-begin-author:taoyan date:2022-11-7 for: issues/4109 平台用户登录失败锁定用户
		if(isLoginFailOvertimes(username)){
			return result.error500("该用户登录失败次数过多,请于10分钟后再次登录!");
		}
		//update-end-author:taoyan date:2022-11-7 for: issues/4109 平台用户登录失败锁定用户
		//update-begin--Author:scott  Date:20190805 for:暂时注释掉密码加密逻辑,有点问题
		//前端密码加密,后端进行密码解密
		//password = AesEncryptUtil.desEncrypt(sysLoginModel.getPassword().replaceAll("%2B", "\\+")).trim();//密码解密
		//update-begin--Author:scott  Date:20190805 for:暂时注释掉密码加密逻辑,有点问题

		//update-begin-author:taoyan date:20190828 for:校验验证码
        String captcha = sysLoginModel.getCaptcha();
        if(captcha==null){
            result.error500("验证码无效");
            return result;
        }
        String lowerCaseCaptcha = captcha.toLowerCase();
        //update-begin-author:taoyan date:2022-9-13 for: VUEN-2245 【漏洞】发现新漏洞待处理20220906
		// 加入密钥作为混淆,避免简单的拼接,被外部利用,用户自定义该密钥即可
        String origin = lowerCaseCaptcha+sysLoginModel.getCheckKey()+jeecgBaseConfig.getSignatureSecret();
		String realKey = Md5Util.md5Encode(origin, "utf-8");
		//update-end-author:taoyan date:2022-9-13 for: VUEN-2245 【漏洞】发现新漏洞待处理20220906
		Object checkCode = redisUtil.get(realKey);
		//当进入登录页时,有一定几率出现验证码错误 #1714
		if(checkCode==null || !checkCode.toString().equals(lowerCaseCaptcha)) {
            log.warn("验证码错误,key= {} , Ui checkCode= {}, Redis checkCode = {}", sysLoginModel.getCheckKey(), lowerCaseCaptcha, checkCode);
			result.error500("验证码错误");
			// 改成特殊的code 便于前端判断
			result.setCode(HttpStatus.PRECONDITION_FAILED.value());
			return result;
		}
		//update-end-author:taoyan date:20190828 for:校验验证码
		
		//1. 校验用户是否有效
		//update-begin-author:wangshuai date:20200601 for: 登录代码验证用户是否注销bug,if条件永远为false
		LambdaQueryWrapper<SysUser> queryWrapper = new LambdaQueryWrapper<>();
		queryWrapper.eq(SysUser::getUsername,username);
		SysUser sysUser = sysUserService.getOne(queryWrapper);
		//update-end-author:wangshuai date:20200601 for: 登录代码验证用户是否注销bug,if条件永远为false
		result = sysUserService.checkUserIsEffective(sysUser);
		if(!result.isSuccess()) {
			return result;
		}

		//2. 校验用户名或密码是否正确
		String userpassword = PasswordUtil.encrypt(username, password, sysUser.getSalt());
		String syspassword = sysUser.getPassword();
		if (!syspassword.equals(userpassword)) {
			//update-begin-author:taoyan date:2022-11-7 for: issues/4109 平台用户登录失败锁定用户
			addLoginFailOvertimes(username);
			//update-end-author:taoyan date:2022-11-7 for: issues/4109 平台用户登录失败锁定用户
			result.error500("用户名或密码错误");
			return result;
		}
				
		//用户登录信息
		userInfo(sysUser, result);
		//update-begin--Author:liusq  Date:20210126  for:登录成功,删除redis中的验证码
		redisUtil.del(realKey);
		//update-begin--Author:liusq  Date:20210126  for:登录成功,删除redis中的验证码
		redisUtil.del(CommonConstant.LOGIN_FAIL + username);
		LoginUser loginUser = new LoginUser();
		BeanUtils.copyProperties(sysUser, loginUser);
		baseCommonService.addLog("用户名: " + username + ",登录成功!", CommonConstant.LOG_TYPE_1, null,loginUser);
        //update-end--Author:wangshuai  Date:20200714  for:登录日志没有记录人员
		return result;
	}

看起来非常多,但学过源码分析课就知道,大部分都是准备过程,关键代码不多。

(源码分析课我看的马士兵教育连鹏举老师的)马士兵全套Spring源码深度解析:AOP、IOC、Bean生命周期、循环依赖、事务、SpringBoot自动装配等_哔哩哔哩_bilibili

前三行功能是,创建返回结果空对象,取用户端传递的用户名和密码(原始密码,未加密):

        Result<JSONObject> result = new Result<JSONObject>();
        String username = sysLoginModel.getUsername();
        String password = sysLoginModel.getPassword();

紧接着一个判断语句,看用户是否频繁登录,这里产生了一个支线任务,为了避免本文过于冗长,只走正常登录相关的主线,有精力再研究支线:

if(isLoginFailOvertimes(username)){
   return result.error500("该用户登录失败次数过多,请于10分钟后再次登录!");
}

接下来取图片验证码,captcha是Completely Automated Public Turing Test to Tell Computers and Humans Apart (全自动区分计算机和人类的图灵测试)的简称,判断来登录的是不是爬虫程序。包含一个if(captcha==null)支线。

String captcha = sysLoginModel.getCaptcha();
if(captcha==null){
    result.error500("验证码无效");
    return result;
}
String lowerCaseCaptcha = captcha.toLowerCase();

下面是判断用户填写验证码是否正确的过程,代码复杂是因为做了加密处理,然后从redis中取验证码的原始字符串,因为这段代码没有生成验证码的过程,我大胆猜测下,图片验证码是根据redis中的checkCode生成的。后面还有一个验证码错误的支线,有精力可以研究。

String origin =lowerCaseCaptcha+sysLoginModel.getCheckKey()+jeecgBaseConfig.getSignatureSecret();

String realKey = Md5Util.md5Encode(origin, "utf-8");

Object checkCode = redisUtil.get(realKey);

if(checkCode==null || !checkCode.toString().equals(lowerCaseCaptcha)) {

log.warn("验证码错误,key= {} , Ui checkCode= {}, Redis checkCode = {}", sysLoginModel.getCheckKey(), lowerCaseCaptcha, checkCode);

result.error500("验证码错误"); // 改成特殊的code 便于前端判断 result.setCode(HttpStatus.PRECONDITION_FAILED.value());

return result; }

login方法代码前一半分析完了,很粗略但正合适,因为这部分只是登录的准备工作,以及一些支线任务,内容很简单,不是login方法的关键点。

下面是核心功能:1. 校验用户是否有效
使用了查询数据库,其中queryWrapper.eq(SysUser::getUsername,username)使用了lambda表达式写法,详见:
MybatisPlus:中QueryWrapper<>().lambda使用(条件查询)_.lambda().eq-CSDN博客
更多关于MybatisPlus的知识可以看:
黑马程序员最新MybatisPlus全套视频教程,4小时快速精通mybatis-plus框架_哔哩哔哩_bilibili

LambdaQueryWrapper<SysUser> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(SysUser::getUsername,username);
SysUser sysUser = sysUserService.getOne(queryWrapper);

result = sysUserService.checkUserIsEffective(sysUser);
if(!result.isSuccess()) {
   return result;
}

sysUserService.checkUserIsEffective(sysUser)是确认用户有效性的方法,可以瞟一眼。

org.jeecg.modules.system.service.impl.SysUserServiceImpl#checkUserIsEffective源码:

	@Override
	public Result<?> checkUserIsEffective(SysUser sysUser) {
		Result<?> result = new Result<Object>();
		//情况1:根据用户信息查询,该用户不存在
		if (sysUser == null) {
			result.error500("该用户不存在,请注册");
			baseCommonService.addLog("用户登录失败,用户不存在!", CommonConstant.LOG_TYPE_1, null);
			return result;
		}
		//情况2:根据用户信息查询,该用户已注销
		//update-begin---author:王帅   Date:20200601  for:if条件永远为falsebug------------
		if (CommonConstant.DEL_FLAG_1.equals(sysUser.getDelFlag())) {
		//update-end---author:王帅   Date:20200601  for:if条件永远为falsebug------------
			baseCommonService.addLog("用户登录失败,用户名:" + sysUser.getUsername() + "已注销!", CommonConstant.LOG_TYPE_1, null);
			result.error500("该用户已注销");
			return result;
		}
		//情况3:根据用户信息查询,该用户已冻结
		if (CommonConstant.USER_FREEZE.equals(sysUser.getStatus())) {
			baseCommonService.addLog("用户登录失败,用户名:" + sysUser.getUsername() + "已冻结!", CommonConstant.LOG_TYPE_1, null);
			result.error500("该用户已冻结");
			return result;
		}
		return result;
	}

都是简单的逻辑判断,判断过程不需要数据库交互。

回到login方法,核心功能2. 校验用户名或密码是否正确,注意从数据库获取的密码是加密后的,前台提交的密码也需要使用相同的MD5加密方式加密,才能验证是否与数据库存储的密码相等。

String userpassword = PasswordUtil.encrypt(username, password, sysUser.getSalt());
String syspassword = sysUser.getPassword();
if (!syspassword.equals(userpassword)) {
   //平台用户登录失败锁定用户
   addLoginFailOvertimes(username);
   //平台用户登录失败锁定用户
   result.error500("用户名或密码错误");
   return result;
}

最后一段执行登录过程,在前面验证都通过后,才完成登录认证过程,包括将登录用户的token存入redis,以便后续请求直接比对token,userInfo(sysUser, result);中执行的就是向redis存入token和其他用户信息的操作。

//用户登录信息
userInfo(sysUser, result);
//登录成功,删除redis中的验证码
redisUtil.del(realKey);
//登录成功,删除redis中的验证码
redisUtil.del(CommonConstant.LOGIN_FAIL + username);
LoginUser loginUser = new LoginUser();
BeanUtils.copyProperties(sysUser, loginUser);
baseCommonService.addLog("用户名: " + username + ",登录成功!", CommonConstant.LOG_TYPE_1, null,loginUser);
      //登录日志没有记录人员
return result;

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1083178.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

RabbitMQ之死信队列解读

目录 基本介绍 消息进入到死信队列的情况 消息过期 队列过期 队列达到最大长度&#xff08;先入队的消息会被发送到DLX&#xff09; 消费者拒绝消息不进行重新投递 消费者拒绝消息 springboot代码实战 实战架构 工程概述 RabbitConfigDeal 配置类&#xff1a;创建队列…

极简c++(9)函数模板与类模板

模板概念 函数模板 注意&#xff1a;不允许template与函数模板定义之间有任何语句&#xff1b; 类模板 没有讲的内容 后续需要可以自己查看补充。

C++ 使用getline()从文件中读取一行字符串

我们知道,getline() 方法定义在 istream 类中,而 fstream 和 ifstream 类继承自 istream 类,因此 fstream 和 ifstream 的类对象可以调用 getline() 成员方法。 当文件流对象调用 getline() 方法时,该方法的功能就变成了从指定文件中读取一行字符串。 该方法有以下 2 种语…

战绩查询、作图工具、干货分享,助你成为吃鸡高手!

大家好&#xff01;吃鸡玩家的福音来了&#xff01;欢迎来到闲游盒的世界&#xff0c;这里有一切你需要的&#xff1a;从战绩查询到作图工具&#xff0c;从干货分享到技巧免费领取&#xff01; 首先&#xff0c;让我们谈谈战绩查询。作为一名玩家&#xff0c;当你在吃鸡游戏中取…

C#,数值计算——数据建模Fitmrq(Levenberg-Marquardt nonlinear fitting)的计算方法与源程序

1 文本格式 using System; namespace Legalsoft.Truffer { /// <summary> /// Levenberg-Marquardt nonlinear fitting /// </summary> public class Fitmrq { private const int NDONE 4; private const int ITMAX 1000; …

【【萌新的SOC学习之GPIO之MIO控制LED实验程序设计】】

萌新的SOC学习之GPIO之MIO控制LED实验程序设计 如何设置完GPIO并且传递数据 我们先了解GPIO引脚的配置 每一个GPIO引脚都可以设置成输入输出 &#xff0c;只有GPIO8 7 只能作为输出 我们现在做一个例子 GPIO 的bank我们知道有4个 bank0 1 2 3 DIRM_0 就是第一个bank 需要写入…

tecplot的使曲线光滑的方法

在对流场模拟结果进行处理时&#xff0c;不可避免的存在些许的噪声导致tecplot的曲线不光滑&#xff0c;故介绍一下tecplot中曲线光滑的方法 1.选择data-alter 2.data中选择smooth,通过调节number of passes&#xff0c;调节曲线光滑程度 大功告成&#xff01; 最近发现写的文…

防反接方案,NMOS PMOS

NMOS电路的缺陷 1.1&#xff0c;由于NMOS具有内阻&#xff0c;我们可以把它设为Rdson, 那么电优先会从低电阻串口处走&#xff0c;这样会有烧毁串口地的风险。 1.2&#xff0c;短期内的方案是&#xff0c;可以在串口处串接1个100ohm电阻。 PMOS电路

基于Springboot实现口腔牙诊所管理平台项目【项目源码+论文说明】

基于Springboot实现口腔牙科诊所管理平台演示 摘要 随着科学技术的飞速发展&#xff0c;社会的方方面面、各行各业都在努力与现代的先进技术接轨&#xff0c;通过科技手段来提高自身的优势&#xff0c;口腔管理平台当然也不能排除在外。口腔管理平台是以实际运用为开发背景&am…

oracle connect by详解

1、作用&#xff1a; 用于存在父子&#xff0c;祖孙&#xff0c;上下级等层级关系的数据表进行层级查询。 2、语法 SELECT ... FROM .... START WITH cond1 CONNECT BY cond2 WHERE cond3;2.1、说明 start with: 指定起始节点的条件 connect by: 指定父子行的条件关系 …

geecg-uniapp 路由修改 页面创建 (2)

首先打开 首页面 &#xff08;1&#xff09;我们以home的常用服务 为例 我们修改 usList 数据 &#xff08;2&#xff09;查找对应路径 work.js 我们修改荒石对应的路径 现在跳转 helloword &#xff08;3&#xff09;修改跳转路径 &#xff08;4&#xff09;创建页面 …

Python —— 接口测试之使用requests发起请求实战

1、认识requests模块 1、requests介绍 requests是一个第三方库&#xff0c;因此首先需要安装这个库&#xff0c;安装三步走&#xff1a; 安装&#xff1a;pip install requests在文件中引用这个模块&#xff1a;import requests使用这个库发起一个请求&#xff08;get请求、…

C++——容器适配器

1. 什么是适配器&#xff1f; 容器适配器是C标准库中的一种数据结构&#xff0c;它可以将不同类型的容器&#xff08;如vector、list、deque等&#xff09;转换为另一种类型的容器。容器适配器提供了一种简单的方式来重新组织和访问数据&#xff0c;同时隐藏了底层容器的实现细…

C++ 多维数组

C 支持多维数组。多维数组声明的一般形式如下&#xff1a; type name[size1][size2]...[sizeN];例如&#xff0c;下面的声明创建了一个三维 5 . 10 . 4 整型数组&#xff1a; int threedim[5][10][4];二维数组 多维数组最简单的形式是二维数组。一个二维数组&#xff0c;在本…

vue启用打印机打印-二维码条形码打印

起因 资产、设备管理必备的二维码条形码打印 原理 所需插件 vue-print-nb 本文版本1.7.5 构建所需要打印的内容&#xff0c;利用vue-print-nb进行打印。二维码条形码打印的本质就是图片打印 代码 html部分 <div ref"printDom"id"printDom">//…

Linux内存管理 | 二、虚拟地址空间布局

我的圈子&#xff1a; 高级工程师聚集地 我是董哥&#xff0c;高级嵌入式软件开发工程师&#xff0c;从事嵌入式Linux驱动开发和系统开发&#xff0c;曾就职于世界500强企业&#xff01; 创作理念&#xff1a;专注分享高质量嵌入式文章&#xff0c;让大家读有所得&#xff01; …

SNAP计算哨兵2号的LAI/FVC/FAPAR

SNAP计算LAI 简介 SNAP的计算LAI方法是基于PROSAIL模型&#xff0c;集成的模块&#xff0c;很方便。 首先要下载SNAP软件&#xff0c;下载步骤见博文 打开数据 找到影像的.xml文件 拖入左边的空白框中&#xff0c;发现所有波段会显示如下。这些数据都是已经经过处理完成之后…

Vue之VueX知识探索(一起了解关于VueX的新世界)

目录 前言 一、VueX简介 1. 什么是VueX 2. VueX的作用及重要性 3. VueX的应用场景 二、VueX的使用准备工作 1. 下载安装VueX 2. vuex获取值以及改变值 2.1 创建所需示例 2.2 将创建好的.vue文件页面显示 2.3 创建VueX的相关文件 2.4 配置VueX四个js文件 2.5 加载到vue示…

网络架构介绍

1 网络 7 层架构 7 层模型主要包括&#xff1a; 1. 物理层&#xff1a;主要定义物理设备标准&#xff0c;如网线的接口类型、光纤的接口类型、各种传输介质的传输速率等。它的主要作用是传输比特流&#xff08;就是由 1、0 转化为电流强弱来进行传输,到达目的地后在转化为1、0…

简述电子设计中的EMC、EMI、ESD

简述电子设计中的EMC、EMI、ESD ESD EMI EMC ESD、EMI、EMC 设计是电子工程师在设计中遇到最常见的难题&#xff0c;电磁兼容性&#xff08;EMC&#xff09;是指设备或系统在其电磁环境中符合要求运行并不对其环境中的任何设备产生无法忍受的电磁干扰的能力。 因此&#xff0…