?
??個(gè)人主頁:? ? ?蒾酒
??系列專欄:《spring boot實(shí)戰(zhàn)》
??山高路遠(yuǎn),行路漫漫,終有歸途
目錄
寫在前面
上文銜接
內(nèi)容簡(jiǎn)介
功能分析
短信驗(yàn)證登錄實(shí)現(xiàn)
1.創(chuàng)建交互對(duì)象
用戶短信登錄/注冊(cè)DTO
創(chuàng)建用戶登錄VO
2.創(chuàng)建自定義業(yè)務(wù)異常
創(chuàng)建驗(yàn)證碼錯(cuò)誤異常
創(chuàng)建用戶被封禁異常
?創(chuàng)建用戶注冊(cè)失敗異常
3.登錄注冊(cè)業(yè)務(wù)邏輯實(shí)現(xiàn)
4.測(cè)試接口
調(diào)用短信驗(yàn)證碼發(fā)送接口
調(diào)用短信驗(yàn)證登錄接口
寫在最后
寫在前面
本文介紹了springboot開發(fā)后端服務(wù)中,短信驗(yàn)證碼登錄功能的設(shè)計(jì)與實(shí)現(xiàn),堅(jiān)持看完相信對(duì)你有幫助。
同時(shí)歡迎訂閱springboot系列專欄,持續(xù)分享spring boot的使用經(jīng)驗(yàn)。
上文銜接
本文銜接上文,可以看一下:
spring boot3登錄開發(fā)-2(2短信驗(yàn)證碼接口實(shí)現(xiàn))-CSDN博客https://blog.csdn.net/qq_62262918/article/details/136888851?spm=1001.2014.3001.5501
用戶表設(shè)計(jì)如下:
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 '賬號(hào)',
user_role varchar(256) default 'user' null comment '用戶角色:user / admin',
avatar varchar(1024) null comment '頭像',
create_time datetime default (now()) null comment '創(chuàng)建時(shí)間',
update_time datetime default CURRENT_TIMESTAMP null comment '更新時(shí)間',
is_delete tinyint(1) default 0 null comment '邏輯刪除:1刪除/0存在',
gender tinyint(1) null comment '性別',
status tinyint(1) default 1 not null comment '狀態(tài):1正常0禁用',
phone varchar(11) null comment '手機(jī)號(hào)'
)
comment '用戶表';
內(nèi)容簡(jiǎn)介
上文我們已經(jīng)實(shí)現(xiàn)了短信驗(yàn)證碼的發(fā)送接口,本文我們來實(shí)現(xiàn)這個(gè)短信驗(yàn)證登錄/注冊(cè)邏輯。
功能分析
- 手機(jī)號(hào)是新手機(jī)號(hào)則先注冊(cè),注冊(cè)完執(zhí)行登錄
- 已經(jīng)注冊(cè)過的手機(jī)號(hào),直接執(zhí)行登錄
短信驗(yàn)證登錄實(shí)現(xiàn)
1.創(chuàng)建交互對(duì)象
用戶短信登錄/注冊(cè)DTO
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Pattern;
import lombok.Data;
/**
* @author mijiupro
*/
@Data
public class UserSmsLoginDTO {
@NotBlank( message = "手機(jī)號(hào)不能為空")
@Pattern(regexp = "^1[3456789]\\d{9}$", message = "手機(jī)號(hào)格式不正確")
private String phone;//手機(jī)號(hào)
@NotBlank( message = "驗(yàn)證碼不能為空")
private String captcha;//驗(yàn)證碼
}
創(chuàng)建用戶登錄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.創(chuàng)建自定義業(yè)務(wù)異常
說白了就是登錄代碼可能會(huì)判斷賬號(hào)是否存在,密碼是否正確,當(dāng)賬號(hào)不存在或密碼錯(cuò)誤需要返回對(duì)應(yīng)提示信息,這種類似情況多了你的代碼就會(huì)很多if-return,代碼就會(huì)很難看;那么通過自定義異常去到異常處理的方法里面寫對(duì)應(yīng)返回提示以及其他邏輯,這樣直接拋出對(duì)應(yīng)異常AOP攔截到該異常走對(duì)應(yīng)異常處理邏輯即可。(一句話概括就是:把處理特殊業(yè)務(wù)異常情況的代碼邏輯抽取出來放到別的類里面寫,可以使代碼更加清晰和可維護(hù))
創(chuàng)建驗(yàn)證碼錯(cuò)誤異常
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;
}
}
創(chuàng)建用戶被封禁異常
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;
}
}
?創(chuàng)建用戶注冊(cè)失敗異常
import com.mijiu.commom.enumerate.ResultEnum;
import lombok.Getter;
/**
* @author mijiupro
*/
@Getter
public class AccountRegisterFailException extends RuntimeException {
private final ResultEnum resultEnum;//返回提示信息枚舉(code,message)
public AccountRegisterFailException(ResultEnum resultEnum) {
this.resultEnum = resultEnum;
}
}
3.登錄注冊(cè)業(yè)務(wù)邏輯實(shí)現(xiàn)
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.UserLoginDTO;
import com.mijiu.commom.model.dto.UserSmsLoginDTO;
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>
* 用戶表 服務(wù)實(shí)現(xiàn)類
* </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 SmsLogin(UserSmsLoginDTO userSmsLoginDTO) {
// 校驗(yàn)驗(yàn)證碼是否存在
HashOperations<String, String, String> hashOps = stringRedisTemplate.opsForHash();
String captcha = hashOps.get("login:sms:captcha:" + userSmsLoginDTO.getPhone(), "captcha");
if (StringUtils.isEmpty(captcha)) {
log.error("手機(jī)號(hào) {} 的驗(yàn)證碼不存在或已過期", userSmsLoginDTO.getPhone());
throw new CaptchaErrorException(ResultEnum.USER_CAPTCHA_NOT_EXIST);
}
// 查詢用戶是否已注冊(cè)
User loginUser = new LambdaQueryChainWrapper<>(userMapper).eq(User::getPhone, userSmsLoginDTO.getPhone()).one();
// 如果未注冊(cè)則進(jìn)行注冊(cè)
if (Objects.isNull(loginUser)) {
loginUser = register(userSmsLoginDTO.getPhone());
}
// 校驗(yàn)驗(yàn)證碼是否正確
if (!userSmsLoginDTO.getCaptcha().equals(captcha)) {
log.error("手機(jī)號(hào) {} 的驗(yàn)證碼錯(cuò)誤", userSmsLoginDTO.getPhone());
throw new CaptchaErrorException(ResultEnum.AUTH_CODE_ERROR);
}
//判斷用戶是否被禁用
if (!loginUser.getStatus()) {
throw new AccountForbiddenException(ResultEnum.USER_ACCOUNT_FORBIDDEN);
}
log.info("手機(jī)號(hào) {} 用戶登錄成功", userSmsLoginDTO.getPhone());
return UserLoginVO.builder()
.token(jwtUtils.generateToken(Map.of("userId", loginUser.getId()), "user"))
.userName(loginUser.getUserName())
.build();
}
private User register(String phone) {
User user = new User();
user.setPhone(phone);
user.setUserName(phone);
user.setStatus(true);
if (userMapper.insert(user) < 1) {
log.error("手機(jī)號(hào) {} 用戶注冊(cè)失??!", phone);
throw new AccountRegisterFailException(ResultEnum.USER_REGISTER_FAIL);
}
log.info("手機(jī)號(hào) {} 用戶注冊(cè)成功", phone);
return user;
}
}
4.測(cè)試接口
使用swagger3進(jìn)行測(cè)試
Spring Boot3整合knife4j(swagger3)_springboot3 knife4j-CSDN博客文章瀏覽閱讀2.3k次,點(diǎn)贊39次,收藏54次。Knife4j · 集Swagger2及OpenAPI3為一體的增強(qiáng)解決方案. | Knife4j (xiaominfo.com)作者的使用的spring boot 3.2.2為當(dāng)前最新版,所以依賴導(dǎo)入最新的knife4j 4.4.0。3.1 增強(qiáng)模式 | Knife4j (xiaominfo.com)好一個(gè)spring boot項(xiàng)目且版本為3X,項(xiàng)目可正常啟動(dòng)??焖匍_始 | Knife4j (xiaominfo.com)接下來配置以下接口文檔的作者等信息。@Tag注解:標(biāo)記接口類別。_springboot3 knife4jhttps://blog.csdn.net/qq_62262918/article/details/135761392?spm=1001.2014.3001.5502
調(diào)用短信驗(yàn)證碼發(fā)送接口
調(diào)用短信驗(yàn)證登錄接口
第一次登錄所以也就自動(dòng)注冊(cè)成功了。
文章來源:http://www.zghlxwxcb.cn/news/detail-844984.html
寫在最后
springboot實(shí)現(xiàn)短信驗(yàn)證登錄注冊(cè)到這里就結(jié)束了,本文介紹了一種通用的短信驗(yàn)證登錄實(shí)現(xiàn)方式,代碼邏輯清晰。任何問題評(píng)論區(qū)或私信討論,歡迎指正。文章來源地址http://www.zghlxwxcb.cn/news/detail-844984.html
到了這里,關(guān)于spring boot3登錄開發(fā)-3(2短信驗(yàn)證登錄/注冊(cè)邏輯實(shí)現(xiàn))的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!