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

本文涉及的产品
云数据库 Tair(兼容Redis),内存型 2GB
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
简介: spring boot3登录开发-3(账密登录逻辑实现)

前置条件

本文衔接上文,请从上文开始spring boot3登录开发-2(1图形验证码接口实现) 用户表设计如下:

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代表被禁用

目录
相关文章
|
13天前
|
人工智能 运维 Java
Spring AI Alibaba Admin 开源!以数据为中心的 Agent 开发平台
Spring AI Alibaba Admin 正式发布!一站式实现 Prompt 管理、动态热更新、评测集构建、自动化评估与全链路可观测,助力企业高效构建可信赖的 AI Agent 应用。开源共建,现已上线!
1105 39
|
3月前
|
前端开发 Java API
利用 Spring WebFlux 技术打造高效非阻塞 API 的完整开发方案与实践技巧
本文介绍了如何使用Spring WebFlux构建高效、可扩展的非阻塞API,涵盖响应式编程核心概念、技术方案设计及具体实现示例,适用于高并发场景下的API开发。
290 0
|
16天前
|
安全 前端开发 Java
《深入理解Spring》:现代Java开发的核心框架
Spring自2003年诞生以来,已成为Java企业级开发的基石,凭借IoC、AOP、声明式编程等核心特性,极大简化了开发复杂度。本系列将深入解析Spring框架核心原理及Spring Boot、Cloud、Security等生态组件,助力开发者构建高效、可扩展的应用体系。(238字)
|
2月前
|
安全 数据可视化 Java
AiPy开发的 Spring 漏洞检测神器,未授权访问无所遁形
针对Spring站点未授权访问问题,现有工具难以检测如Swagger、Actuator等组件漏洞,且缺乏修复建议。全新AI工具基于Aipy开发,具备图形界面,支持一键扫描常见Spring组件,自动识别未授权访问风险,按漏洞类型标注并提供修复方案,扫描结果可视化展示,支持导出报告,大幅提升渗透测试与漏洞定位效率。
|
3月前
|
缓存 Java API
Spring WebFlux 2025 实操指南详解高性能非阻塞 API 开发全流程核心技巧
本指南基于Spring WebFlux 2025最新技术栈,详解如何构建高性能非阻塞API。涵盖环境搭建、响应式数据访问、注解与函数式两种API开发模式、响应式客户端使用、测试方法及性能优化技巧,助你掌握Spring WebFlux全流程开发核心实践。
536 0
|
3月前
|
存储 NoSQL Java
探索Spring Boot的函数式Web应用开发
通过这种方式,开发者能以声明式和函数式的编程习惯,构建高效、易测试、并发友好的Web应用,同时也能以较小的学习曲线迅速上手,因为这些概念与Spring Framework其他部分保持一致性。在设计和编码过程中,保持代码的简洁性和高内聚性,有助于维持项目的可管理性,也便于其他开发者阅读和理解。
106 0
|
5月前
|
Java API 网络架构
基于 Spring Boot 框架开发 REST API 接口实践指南
本文详解基于Spring Boot 3.x构建REST API的完整开发流程,涵盖环境搭建、领域建模、响应式编程、安全控制、容器化部署及性能优化等关键环节,助力开发者打造高效稳定的后端服务。
667 1
|
5月前
|
Java API 微服务
Java 21 与 Spring Boot 3.2 微服务开发从入门到精通实操指南
《Java 21与Spring Boot 3.2微服务开发实践》摘要: 本文基于Java 21和Spring Boot 3.2最新特性,通过完整代码示例展示了微服务开发全流程。主要内容包括:1) 使用Spring Initializr初始化项目,集成Web、JPA、H2等组件;2) 配置虚拟线程支持高并发;3) 采用记录类优化DTO设计;4) 实现JPA Repository与Stream API数据访问;5) 服务层整合虚拟线程异步处理和结构化并发;6) 构建RESTful API并使用Springdoc生成文档。文中特别演示了虚拟线程配置(@Async)和StructuredTaskSco
523 0

热门文章

最新文章

下一篇
开通oss服务