spring boot3登录开发-3(账密登录逻辑实现)

⛰️个人主页: 蒾酒
🔥系列专栏:《spring boot实战》
🌊山高路远,行路漫漫,终有归途。
目录
前置条件
内容简介
用户登录逻辑实现
创建交互对象
1.创建用户登录DTO
2.创建用户登录VO
创建自定义登录业务异常
1.创建验证码错误异常
2.创建用户不存在异常
3.创建密码错误异常
4.创建用户被封禁异常
2.登录业务逻辑实现
3.测试接口
前置条件
本文衔接上文,请从上文开始
spring boot3登录开发-2(1图形验证码接口实现)-CSDN博客
https://blog.csdn.net/qq_62262918/article/details/136064820?spm=1001.2014.3001.5502用户表设计如下:
create table user
(id bigint auto_increment comment '主键'primary key,user_name varchar(32) null comment '用户昵称',password varchar(256) null comment '密码',user_account varchar(64) null comment '账号',user_role varchar(256) default 'user' null comment '用户角色:user / admin',avatar varchar(1024) null comment '头像',create_time datetime default (now()) null comment '创建时间',update_time datetime default CURRENT_TIMESTAMP null comment '更新时间',is_delete tinyint(1) default 0 null comment '逻辑删除:1删除/0存在',gender tinyint(1) null comment '性别',status tinyint(1) default 1 not null comment '状态:1正常0禁用'
)comment '用户表';INSERT INTO `user` VALUES (1,'蒾酒','e10adc3949ba59abbe56e057f20f883e','admin','admin',NULL,'2024-02-02 18:54:44','2024-02-02 18:54:44',0,NULL,1);
内容简介
上文我们已经实现了图形验证码接口,本文我们实现登录逻辑
- 通过用户登录DTO(数据传输对象)接收用户登录填写信息
- 通过注解@NotNull、@Valid进行参数非空校验
- 通过redis缓存的验证码信息与用户提交的比对验证
- 通过全局异常处理处理参数为空、用户不存在、密码错误、验证码错误、用户被封禁等业务异常
用户登录逻辑实现
作者习惯于将业务代码全部放在service层里面,controller层只用于暴漏接口封装返回结果。
创建交互对象
1.创建用户登录DTO
说白了就是用户登录表单提交发起post请求,请求体负载的登录对象后端需要有个对象跟表单对象字段对应接收:请求体json->接收对象。这一过程也叫反序列化。
通过@NotNull做非空校验
import jakarta.validation.constraints.NotNull;
import lombok.Data;/*** @author mijiupro*/
@Data
public class UserLoginDTO {@NotNull(message = "账号不能为空")private String userAccount;//用户账号@NotNull(message = "密码不能为空")private String password;//密码@NotNull(message = "验证码id不能为空")private String captchaId;//验证码id@NotNull(message = "验证码内容不能为空")private String captcha;//验证码内容
}
2.创建用户登录VO
通俗来说就是用户登录成功后返回给前端一个合法令牌token,以及一些非敏感信息方便前端展示,这些信息包装成一个对象,展示对象->json。这一过程又叫序列化。
import lombok.Builder;
import lombok.Data;import java.io.Serializable;/*** @author mijiupro*/
@Data
@Builder
public class UserLoginVO implements Serializable {private String token;//令牌private String userName;//用户名private String avatar;//头像
}
创建自定义登录业务异常
说白了就是登录代码可能会判断账号是否存在密码是否正确,当账号不存在或密码错误需要返回对应提示信息,这种类似情况多了你的代码就会很多if-return,代码就会很难看;那么通过自定义异常去到异常处理的方法里面写对应返回提示以及其他逻辑,这样直接抛出对应异常AOP拦截到该异常走对应异常处理逻辑即可。(一句话概括就是:把处理特殊业务异常情况的代码逻辑抽取出来放到别的类里面写,可以使代码更加清晰和可维护)
1.创建验证码错误异常
import com.mijiu.commom.enumerate.ResultEnum;
import lombok.Getter;/*** @author mijiupro*/
@Getter
public class CaptchaErrorException extends RuntimeException {private final ResultEnum resultEnum;//返回提示信息枚举(code,message)public CaptchaErrorException(ResultEnum resultEnum) {this.resultEnum = resultEnum;}
}
2.创建用户不存在异常
import com.mijiu.commom.enumerate.ResultEnum;
import lombok.Getter;/*** 账户不存在异常** @author mijiupro*/
@Getter
public class AccountNotFoundException extends RuntimeException {private final ResultEnum resultEnum;public AccountNotFoundException(ResultEnum resultEnum) {this.resultEnum = resultEnum;}
}
3.创建密码错误异常
import com.mijiu.commom.enumerate.ResultEnum;
import lombok.Getter;/*** 密码错误异常** @author mijiupro*/
@Getter
public class PasswordErrorException extends RuntimeException {private final ResultEnum resultEnum;public PasswordErrorException(ResultEnum resultEnum) {this.resultEnum = resultEnum;}}
4.创建用户被封禁异常
import com.mijiu.commom.enumerate.ResultEnum;
import lombok.Getter;/*** @author mijiupro*/
@Getter
public class AccountForbiddenException extends RuntimeException {private final ResultEnum resultEnum;public AccountForbiddenException(ResultEnum resultEnum) {this.resultEnum = resultEnum;}
}
2.登录业务逻辑实现
代码逻辑:参数校验(使用注解方式校验)----验证码校验----账号存在检验----密码校验----用户状态判断
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.mijiu.commom.enumerate.ResultEnum;
import com.mijiu.commom.exception.AccountForbiddenException;
import com.mijiu.commom.exception.AccountNotFoundException;
import com.mijiu.commom.exception.CaptchaErrorException;
import com.mijiu.commom.exception.PasswordErrorException;
import com.mijiu.commom.model.dto.UserLoginDTO;
import com.mijiu.commom.model.vo.UserLoginVO;
import com.mijiu.commom.util.JwtUtils;
import com.mijiu.entity.User;
import com.mijiu.mapper.UserMapper;
import com.mijiu.service.UserService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import jakarta.validation.Valid;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.DigestUtils;import java.util.Map;/*** <p>* 用户表 服务实现类* </p>** @author 蒾酒* @since 2024-02-03*/
@Service
@Slf4j
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {private final UserMapper userMapper;private final JwtUtils jwtUtils;private final StringRedisTemplate stringRedisTemplate;public UserServiceImpl(UserMapper userMapper, JwtUtils jwtUtils, StringRedisTemplate stringRedisTemplate) {this.userMapper = userMapper;this.jwtUtils = jwtUtils;this.stringRedisTemplate = stringRedisTemplate;}@Overridepublic UserLoginVO login(@Valid UserLoginDTO userLoginDTO) {// 获取验证码idString captchaId = userLoginDTO.getCaptchaId();// 获取用户提交验证码String userCaptcha = userLoginDTO.getCaptcha();// 获取缓存验证码String cacheCaptcha = stringRedisTemplate.opsForValue().get("login:captcha:" + captchaId);// 比较验证码是否正确if (cacheCaptcha == null || !cacheCaptcha.equalsIgnoreCase(userCaptcha)) {throw new CaptchaErrorException(ResultEnum.USER_CAPTCHA_ERROR);}// 判断用户是否存在User loginUser = new LambdaQueryChainWrapper<>(userMapper).select(User::getId, User::getUserAccount, User::getPassword,User::getUserName, User::getUserRole,User::getAvatar, User::getStatus).eq(User::getUserAccount, userLoginDTO.getUserAccount()).one();if (loginUser == null) {throw new AccountNotFoundException(ResultEnum.USER_NOT_EXIST);}log.info("loginUser: {}", loginUser);// 判断密码是否正确String md5Password = DigestUtils.md5DigestAsHex(userLoginDTO.getPassword().getBytes());if (!md5Password.equals(loginUser.getPassword())) {throw new PasswordErrorException(ResultEnum.USER_PASSWORD_ERROR);}// 判断用户状态是否正常if (!loginUser.getStatus()) {throw new AccountForbiddenException(ResultEnum.USER_ACCOUNT_FORBIDDEN);}// 生成tokenString token = jwtUtils.generateToken(Map.of("userId", loginUser.getId(),"userRole",loginUser.getUserRole()),"user");//构建响应对象return UserLoginVO.builder().userName(loginUser.getUserName()).avatar(loginUser.getAvatar()).token(token).build();}
}
这里要说一下的是通常数据库不放密码明文,这样做可以防止别人获取数据库直接得到账密登录违规操作风险,代码中使用MD5加密是很容易被暴力破解的所以可以用MD5加盐策略或者其他安全加密算法。
3.测试接口
测试之前记得把图形验证码接口中redis缓存验证码的过期时间设置的长一点。
先生成一个验证码

日志打印图形验证码文本
![]()
正常测试

验证码错误测试

用户不存在测试

密码错误测试

账号被封禁测试
字段status修改为0代表被禁用


相关文章:
spring boot3登录开发-3(账密登录逻辑实现)
⛰️个人主页: 蒾酒 🔥系列专栏:《spring boot实战》 🌊山高路远,行路漫漫,终有归途。 目录 前置条件 内容简介 用户登录逻辑实现 创建交互对象 1.创建用户登录DTO 2.创建用户登录VO 创建自定义登录业务异…...
Django后端开发——ORM
文章目录 参考资料ORM-基础字段及选项字段类型练习——添加模型类应用bookstore下的models.py数据库迁移——同步至mysqlmysql中查看效果字段选项Meta类定义示例:改表名应用bookstore下的models.py终端效果练习——改表名+字段选项修改应用bookstore下的models.py终端效果ORM基…...
AI模型训练的初步整理
明天会有人来给我们讲AI方面的课,我也一直想整理一下这方面的知识,今天也趁着这个机会做一下功课,算是预习。 首先,AI的模型训练可以分为: 增量学习(Incremental Learning) 增量学习允许模型在…...
【Java从入门到精通】Java Number Math 类
Java Number & Math 类 一般地,当需要使用数字的时候,我们通常使用内置数据类型,如:byte、int、long、double 等。 实例 int a 5000; float b 13.65f; byte c 0x4a; 然而,在实际开发过程中,我们…...
SQL字符集
目标:了解字符集的概念,掌握MySQL数据库存储数据的字符集逻辑以及设置方式 字符集概念 MySQL字符集关系 解决乱码问题 字符集设置原理 1、字符集概念 目标:了解字符集概念,掌握字符集存储和读取的实现原理 概念 字符集:charset或者character set&am…...
openssl 生成nginx自签名的证书
1、命令介绍 openssl req命令主要的功能有,生成证书请求文件, 查看验证证书请求文件,还有就是生成自签名证书。 主要参数 主要命令选项: -new :说明生成证书请求文件 -x509 :说明生成自签名证书 -key :指定已…...
adb push 使用
adb push命令用于将文件从本地计算机推送到Android设备。要使用adb push命令,需要先连接Android设备并启动ADB调试模式。以下是使用adb push命令的基本步骤: 打开终端(命令提示符)。 使用cd命令导航到存储要推送文件的文件夹。 …...
【Docker】构建pytest-playwright镜像并验证
Dockerfile FROM ubuntu LABEL maintainer "langhuang521l63.com" ENV TZAsia/Shanghai #设置时区 #安装python3依赖与下载安装包 RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone \&& apt update \&&…...
算法沉淀——穷举、暴搜、深搜、回溯、剪枝综合练习四(leetcode真题剖析)
算法沉淀——穷举、暴搜、深搜、回溯、剪枝综合练习四 01.解数独02.单词搜索03.黄金矿工04.不同路径 III 01.解数独 题目链接:https://leetcode.cn/problems/sudoku-solver/ 编写一个程序,通过填充空格来解决数独问题。 数独的解法需 遵循如下规则&am…...
如何在java中使用 Excel 动态函数生成依赖列表
前言 在Excel 中,依赖列表或级联下拉列表表示两个或多个列表,其中一个列表的项根据另一个列表而变化。依赖列表通常用于Excel的业务报告,例如学术记分卡中的【班级-学生】列表、区域销售报告中的【区域-国家/地区】列表、人口仪表板中的【年…...
07 MyBatis之高级映射 + 懒加载(延迟加载)+缓存
1. 高级映射 例如有两张表, 分别为班级表和学生表 自然, 一个班级对应多个学生 像这种数据 , 应该如果如何映射到Java的实体类上呢? 这就是高级映射解决的问题 以班级和学生为例子 , 因为一个班级对应多个学生 , 因此学生表中必定有一个班级编号字段cid 但我们在学生的实体…...
MT8791迅鲲900T联发科5G安卓核心板规格参数_MTK平台方案定制
MT8791安卓核心板是一款搭载了旗舰级配置的中端手机芯片。该核心板采用了八核CPU架构设计,但是升级了旗舰级的Arm Cortex-A78核心,两个大核主频最高可达2.4GHz。配备了Arm Mali-G68 GPU,通过Mali-G88的先进技术,图形处理性能大幅提…...
java:Java中的数组详解
目录 Java数组的定义和特点: Java数组的初始化和赋值 Java数组的常用操作 1. 遍历数组 2. 获取数组长度 3. 访问数组元素 4. 数组的拷贝 多维数组 数组的排序和查找 冒泡排序: 快速排序 : 二分查找: 数组的应用: Java数…...
Modern C++ std::visit从实践到原理
前言 std::visit 是 C17 中引入的一个模板函数,它用于对给定的 variant、union 类型或任何其他兼容的类型执行一个访问者操作。这个函数为多种可能类型的值提供了一种统一的访问机制。使用 std::visit,你可以编写更通用和灵活的代码,而无需关…...
谷歌gemma2b windows本地cpu gpu部署,pytorch框架,模型文件百度网盘下载
简介 谷歌DeepMind发布了Gemma,这是一系列灵感来自用于Gemini相同研究和技术的开放模型。开放模型适用于各种用例,这是谷歌非常明智的举措。有2B(在2T tokens上训练)和7B(在6T tokens上训练)模型,包括基础和指令调整版本。在8192个token的上下文长度上进行训练。允许商业使…...
数据结构-查找与排序
数据结构再往后就是比较零散的各种操作,查找与排序是其中最常出现的,今天来总结一下常用的查找与排序所用的方法 查找 顺序查找 最简单的查找方式,遍历,然后比较 bool search1(int *a,int n,int k){for (int i1;i<n;i){//遍…...
【前端素材】推荐优质后台管理系统Qovex平台模板(附源码)
一、需求分析 1、定义 后台管理系统是一种用于管理和监控网站、应用程序或系统的在线工具。它通常是通过网页界面进行访问和操作,用于管理网站内容、用户权限、数据分析等。后台管理系统是网站或应用程序的控制中心,管理员可以通过后台系统进行各种管理…...
MATLAB环境下基于短时傅里叶变换和Rényi熵的脑电信号和语音信号分析
傅里叶变换是不能很好的反映信号在时域的某一个局部范围的频谱特点的,这一点很可惜。因为在许多实际工程中,人们对信号在局部区域的特征是比较关心的,这些特征包含着十分有用的信息。这类信号因为在时域(或者是空间域)上具有突变的非稳定性和…...
Go语言调用身份证实名认证API方法-标准版身份证实名认证接口
翔云身份证实名认证接口具备高准确度的身份信息比对能力,包括姓名、身份证号码、人脸照片等信息的一致性验证,并能实时反馈验证结果。 以下是GO语言调用翔云身份实名认证API的代码: package mainimport ("fmt""bytes"&q…...
数据库增删改查
DDL: 数据定义语言,用来定义数据库对象(数据库、表、字段)DML: 数据操作语言,用来对数据库表中的数据进行增删改DQL: 数据查询语言,用来查询数据库中表的记录DCL: 数据控制语言,用来创建数据库用户、控制数…...
后进先出(LIFO)详解
LIFO 是 Last In, First Out 的缩写,中文译为后进先出。这是一种数据结构的工作原则,类似于一摞盘子或一叠书本: 最后放进去的元素最先出来 -想象往筒状容器里放盘子: (1)你放进的最后一个盘子(…...
C++_核心编程_多态案例二-制作饮品
#include <iostream> #include <string> using namespace std;/*制作饮品的大致流程为:煮水 - 冲泡 - 倒入杯中 - 加入辅料 利用多态技术实现本案例,提供抽象制作饮品基类,提供子类制作咖啡和茶叶*//*基类*/ class AbstractDr…...
ES6从入门到精通:前言
ES6简介 ES6(ECMAScript 2015)是JavaScript语言的重大更新,引入了许多新特性,包括语法糖、新数据类型、模块化支持等,显著提升了开发效率和代码可维护性。 核心知识点概览 变量声明 let 和 const 取代 var…...
PPT|230页| 制造集团企业供应链端到端的数字化解决方案:从需求到结算的全链路业务闭环构建
制造业采购供应链管理是企业运营的核心环节,供应链协同管理在供应链上下游企业之间建立紧密的合作关系,通过信息共享、资源整合、业务协同等方式,实现供应链的全面管理和优化,提高供应链的效率和透明度,降低供应链的成…...
前端导出带有合并单元格的列表
// 导出async function exportExcel(fileName "共识调整.xlsx") {// 所有数据const exportData await getAllMainData();// 表头内容let fitstTitleList [];const secondTitleList [];allColumns.value.forEach(column > {if (!column.children) {fitstTitleL…...
OkHttp 中实现断点续传 demo
在 OkHttp 中实现断点续传主要通过以下步骤完成,核心是利用 HTTP 协议的 Range 请求头指定下载范围: 实现原理 Range 请求头:向服务器请求文件的特定字节范围(如 Range: bytes1024-) 本地文件记录:保存已…...
页面渲染流程与性能优化
页面渲染流程与性能优化详解(完整版) 一、现代浏览器渲染流程(详细说明) 1. 构建DOM树 浏览器接收到HTML文档后,会逐步解析并构建DOM(Document Object Model)树。具体过程如下: (…...
论文浅尝 | 基于判别指令微调生成式大语言模型的知识图谱补全方法(ISWC2024)
笔记整理:刘治强,浙江大学硕士生,研究方向为知识图谱表示学习,大语言模型 论文链接:http://arxiv.org/abs/2407.16127 发表会议:ISWC 2024 1. 动机 传统的知识图谱补全(KGC)模型通过…...
从零实现STL哈希容器:unordered_map/unordered_set封装详解
本篇文章是对C学习的STL哈希容器自主实现部分的学习分享 希望也能为你带来些帮助~ 那咱们废话不多说,直接开始吧! 一、源码结构分析 1. SGISTL30实现剖析 // hash_set核心结构 template <class Value, class HashFcn, ...> class hash_set {ty…...
NLP学习路线图(二十三):长短期记忆网络(LSTM)
在自然语言处理(NLP)领域,我们时刻面临着处理序列数据的核心挑战。无论是理解句子的结构、分析文本的情感,还是实现语言的翻译,都需要模型能够捕捉词语之间依时序产生的复杂依赖关系。传统的神经网络结构在处理这种序列依赖时显得力不从心,而循环神经网络(RNN) 曾被视为…...
