🔥系列专栏:《spring boot实战》
目录
写在前面
本文介绍了springboot开发后端服务中,邮箱验证码登录/注册功能的设计与实现,坚持看完相信对你有帮助。
同时欢迎订阅springboot系列专栏,持续分享spring boot的使用经验。
上文衔接
本文衔接上文,可以看一下:
用户表设计如下:
create table user
(
id bigint auto_increment comment '主键'
primary key,
user_name varchar(32) null comment '用户昵称',
password varchar(255) null comment '密码',
account varchar(64) null comment '账号',
user_role varchar(252) 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 '性别',
user_signature varchar(255) null comment '个性签名',
status tinyint(1) default 1 not null comment '状态:1正常0禁用',
phone varchar(11) null comment '手机号',
email varchar(100) null comment '邮箱'
)
comment '用户表';
内容简介
上文我们已经实现了邮件验证码的发送接口,本文我们来实现这个邮箱验证登录/注册逻辑。
功能分析
- 邮箱未注册过则先注册,注册完执行登录
- 已经注册过的邮箱,直接执行登录
所需依赖
redis
邮箱验证登录/注册实现
1.创建交互对象
用户登录/注册DTO:
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Pattern;
import lombok.Data;
/**
* @author mijiupro
*/
@Data
public class UserEmailLoginDTO {
@NotBlank(message = "邮箱不能为空")
@Pattern(regexp = "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$", message = "邮箱格式不正确")
private String email;
@NotBlank( message = "验证码不能为空")
private String captcha;
}
这里用Lombok的@Data来生成getter和setter。
这里用spring boot参数验证组件来检验参数格式:
用户登录VO:
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;//头像
}
2.登录注册业务逻辑实现
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.mijiu.commom.enumerate.ResultEnum;
import com.mijiu.commom.exception.*;
import com.mijiu.commom.model.dto.UserEmailLoginDTO;
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 lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.DigestUtils;
import java.util.Map;
import java.util.Objects;
/**
* <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 emailLogin(UserEmailLoginDTO userEmailLoginDTO) {
// 校验验证码是否存在
HashOperations<String, String, String> hashOps = stringRedisTemplate.opsForHash();
String captcha = hashOps.get("login:email:captcha:" + userEmailLoginDTO.getEmail(), "captcha");
if (StringUtils.isEmpty(captcha)) {
log.error("邮箱 {} 的验证码不存在或已过期", userEmailLoginDTO.getEmail());
throw new CaptchaErrorException(ResultEnum.USER_CAPTCHA_NOT_EXIST);
}
// 查询用户是否已注册
User loginUser = new LambdaQueryChainWrapper<>(userMapper).eq(User::getPhone, userEmailLoginDTO.getEmail()).one();
// 如果未注册则进行注册
if (Objects.isNull(loginUser)) {
loginUser = registerByEmail(userEmailLoginDTO.getEmail());
}
// 校验验证码是否正确
if (!userEmailLoginDTO.getCaptcha().equals(captcha)) {
log.error("邮箱号 {} 的验证码错误", userEmailLoginDTO.getEmail());
throw new CaptchaErrorException(ResultEnum.AUTH_CODE_ERROR);
}
//判断用户是否被禁用
if (!loginUser.getStatus()) {
throw new AccountForbiddenException(ResultEnum.USER_ACCOUNT_FORBIDDEN);
}
log.info("邮箱 {} 用户登录成功",userEmailLoginDTO.getEmail());
return UserLoginVO.builder()
.token(jwtUtils.generateToken(Map.of("userId", loginUser.getId()), "user"))
.userName(loginUser.getUserName())
.build();
}
// 用户邮箱注册
private User registerByEmail(String email) {
User user = new User();
user.setEmail(email);
user.setUserName(email);
user.setStatus(true);
if (userMapper.insert(user) < 1) {
log.error("邮箱 {} 用户注册失败!", email);
throw new AccountRegisterFailException(ResultEnum.USER_REGISTER_FAIL);
}
log.info("邮箱 {} 用户注册成功", email);
return user;
}
}
4.测试接口
使用swagger3进行测试
Spring Boot3整合knife4j(swagger3)_springboot3 knife4j-CSDN博客
调用邮箱验证码发送接口
调用邮箱验证登录接口
第一次登录所以也就自动注册成功了。
写在最后
springboot实现邮箱验证登录注册到这里就结束了,本文介绍了一种通用的邮箱验证登录实现方式,代码逻辑清晰。任何问题评论区或私信讨论,欢迎指正。