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

news2024/11/20 6:31:00

 

⛰️个人主页:     蒾酒

🔥系列专栏:《spring boot实战》

🌊山高路远,行路漫漫,终有归途。


目录

前置条件

内容简介

用户登录逻辑实现

创建交互对象

1.创建用户登录DTO

2.创建用户登录VO

创建自定义登录业务异常

1.创建验证码错误异常

2.创建用户不存在异常

3.创建密码错误异常

4.创建用户被封禁异常

2.登录业务逻辑实现

3.测试接口


前置条件

本文衔接上文,请从上文开始

spring boot3登录开发-2(1图形验证码接口实现)-CSDN博客icon-default.png?t=N7T8https://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;
    }
    @Override
    public UserLoginVO login(@Valid UserLoginDTO userLoginDTO) {
        // 获取验证码id
        String 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);
        }
        // 生成token
        String 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代表被禁用

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

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

相关文章

H12-821_45

45.如图所示,同一局域网中的四台路由器运行IS-IS,其中R1是DIS.则R2、R3、R4分别和R1建立邻接关系,R2、R3、R4之间不建立邻接关系。 A.正确 B.错误 答案&#xff1a;B 注释&#xff1a; 在广播链路上IS-IS路由器建立邻接关系和OSPF不同&#xff0c;所有IS-IS路由器之间都可以建…

Github 2024-02-23 开源项目日报 Top10

根据Github Trendings的统计&#xff0c;今日(2024-02-23统计)共有10个项目上榜。根据开发语言中项目的数量&#xff0c;汇总情况如下&#xff1a; 开发语言项目数量非开发语言项目4Python项目3TypeScript项目1HTML项目1Dart项目1Rust项目1 从零开始构建你喜爱的技术 创建周…

关于el-select值的回显问题 : 框内显示label值还是value值

<el-form-item label"状态" prop""><el-selectv-model"roleForm.state"class"m-2"size"large"style"width: 240px"placeholder"请选择状态"value-key"value"//value-key 与下面的ke…

基于SVM的功率分类,基于支持向量机SVM的功率分类识别,Libsvm工具箱详解

目录 支持向量机SVM的详细原理 SVM的定义 SVM理论 Libsvm工具箱详解 简介 参数说明 易错及常见问题 完整代码和数据下载链接:基于SVM的功率分类,基于支持向量机SVM的功率分类识别资源-CSDN文库 https://download.csdn.net/download/abc991835105/88862836 SVM应用实例, 基于…

RK3568平台开发系列讲解(Linux系统篇)SPI 客户端通信

🚀返回专栏总目录 文章目录 一、spi_transfer二、spi_message三、初始化沉淀、分享、成长,让自己和他人都能有所收获!😄 SPI I/O模型由一组队列消息组成。我们提交一个或多个struct spi_message结构时,这些结构以同步或异步方式处理完成。单个消息由一个或多个struct sp…

Swift Combine 使用调试器调试管道 从入门到精通二十六

Combine 系列 Swift Combine 从入门到精通一Swift Combine 发布者订阅者操作者 从入门到精通二Swift Combine 管道 从入门到精通三Swift Combine 发布者publisher的生命周期 从入门到精通四Swift Combine 操作符operations和Subjects发布者的生命周期 从入门到精通五Swift Com…

Linux的ACL权限以及特殊位和隐藏属性

前言&#xff1a; ACL是什么&#xff1f; ACL&#xff08;Access Control List&#xff09;是一种权限控制机制&#xff0c;用于在Linux系统中对文件和目录进行细粒度的访问控制。传统的Linux权限控制机制基于所有者、所属组和其他用户的三个权限类别&#xff08;读、写、执行…

今日arXiv最热NLP大模型论文:无需提示也能推理!Google DeepMind新研究揭示AI内在推理能力

在人工智能领域&#xff0c;大语言模型&#xff08;LLMs&#xff09;已经在各种复杂的推理基准测试中展现出了令人瞩目的性能。传统上&#xff0c;这些推理能力是通过精心设计的提示技术来激发的&#xff0c;例如少量示例提示&#xff08;few-shot prompting&#xff09;或零示…

开源的表单设计器拥有什么显著特点?

开源的表单设计器的特点是什么&#xff1f;广州流辰信息是专业研发低代码技术平台的服务商&#xff0c;可以为企业提供系统开发、数据治理、数据分析各环节技术和方案支撑。为了帮助大家了解开源的表单设计器的相关优势特点&#xff0c;小编将为大家做一个详细介绍。 什么是开源…

Java编程实战:构建医疗信息管理新平台

✍✍计算机编程指导师 ⭐⭐个人介绍&#xff1a;自己非常喜欢研究技术问题&#xff01;专业做Java、Python、微信小程序、安卓、大数据、爬虫、Golang、大屏等实战项目。 ⛽⛽实战项目&#xff1a;有源码或者技术上的问题欢迎在评论区一起讨论交流&#xff01; ⚡⚡ Java实战 |…

面试经典150题 -- 二叉树 (总结)

总的地址 : 面试经典 150 题 - 学习计划 - 力扣&#xff08;LeetCode&#xff09;全球极客挚爱的技术成长平台 104 . 二叉树的最大深度 104 . 二叉树的最大深度 递归 : 直接用递归访问 &#xff0c; 访问左孩子 和 右孩子 &#xff0c; 如果 存在 &#xff0c; 深度就1 &…

SpringBoot-2.7.6基于SLF4J日志门面的日志框架切换

SpringBoot 没有强制性的日志记录依赖项,但 Commons Logging API 除外,它通常由 Spring Framework 的模块提供。 要使用 Logback,您需要将其包含在类路径中。 推荐的方法是您只需要通过启动器,这都取决于 . 对于 Web 应用程序 ,因为它可传递地依赖于日志记录启动器。 如果…

【C语言】指针变量未初始化

我们知道&#xff1a;全局变量未赋初值&#xff0c;编译器会直接赋值为0&#xff1b;局部变量如果未赋初值&#xff0c;则会维持上一状态保存在该地址上的值&#xff0c;这个值是随机的。把这个值赋值给局部变量是没有意义的。 但是指针变量是如何解决不赋初值&#xff1f; 指…

linux高级作业

作业需求 1、openEuler 二进制方式安装MySQL 8.0.x。 二、备份数据库 3.备份数据库school到/backup目录 4.备份MySQL数据库为带删除表的格式&#xff0c;能够让该备份覆盖已有数据库而不需要手动删除原有数据库 5.直接将MySQL数据库压缩备份 第一题 1、openEuler 二进制方式…

XL6009是什么芯片?一文带你了解XL6009引脚说明、数据参数的解读

XL6009是一款高性能、高效率的降压升压转换器芯片。它可以将输入电压范围从3.5V至32V的电源转换为可调的输出电压范围从1.25V至35V。 XL6009引脚说明 引脚说明如下&#xff1a; VIN&#xff1a;输入电压引脚&#xff0c;接入供电电源的正极。GND&#xff1a;地引脚&#xff0…

ubuntu20.04 安装 matlab R2023b

ubuntu20.04 使用matlab R2023b 起因步骤问题问题1问题2问题3 起因 闲着没事&#xff0c;想在ubuntu上安装matlab。 步骤 这个博客写得很好&#xff0c;我就不赘述了&#xff1a;参考博客 。但有点不一样&#xff1a;我现在matlab官网上下载的linux版本不是iso镜像文件&…

稀疏计算、彩票假说、MoE、SparseGPT

稀疏计算可能是未来10年内最有潜力的深度学习方向之一&#xff0c;稀疏计算模拟了对人脑的观察&#xff0c;人脑在处理信息的时候只有少数神经元在活动&#xff0c;多数神经元是不工作的。而稀疏计算的基本思想是&#xff1a;在计算过程中&#xff0c;将一些不重要的参数设置为…

基于DPU和HADOS-RACE加速Spark 3.x

背景简介 Apache Spark&#xff08;下文简称Spark&#xff09;是一种开源集群计算引擎&#xff0c;支持批/流计算、SQL分析、机器学习、图计算等计算范式&#xff0c;以其强大的容错能力、可扩展性、函数式API、多语言支持&#xff08;SQL、Python、Java、Scala、R&#xff09…

C++11新特性 lambda表达式与模板函数 std::make_shared

一&#xff1a;make_shared example1: auto l_size make_shared<std::array<int, 2> >(); example2: m_timeHandlePtr make_shared<svTimerHandle>(renderPtr->GetRenderWindow()->GetInteractor(), m_BatchCallBack); C11 中引入了智能指针, 同…

JavaWeb——004Maven SpringBootWeb入门

一、Maven 1、什么是maven&#xff1f; 2、Maven的作用是什么&#xff1f;&#xff08;3种&#xff09; 1.1、方便的依赖管理 依赖管理&#xff1a;有了Maven&#xff0c;我们就不用再手动导入Jar包了&#xff0c;我们只需要在配置文件当中&#xff0c;简单描述一下项目所需要…