北京加盟网站建设,电子商务网站建设 项目规划书,电脑网页尺寸一般是多少,更改网站图片⛰️个人主页: 蒾酒
#x1f525;系列专栏#xff1a;《spring boot实战》
#x1f30a;山高路远#xff0c;行路漫漫#xff0c;终有归途。 目录
前置条件
内容简介
用户登录逻辑实现
创建交互对象
1.创建用户登录DTO
2.创建用户登录VO
创建自定义登录业务异…
⛰️个人主页: 蒾酒
系列专栏《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?spm1001.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;//验证码idNotNull(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 ServiceImplUserMapper, 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代表被禁用