基於 Session 實現簡訊登入

柒墨轩發表於2024-07-23

簡訊驗證

一、基於Session

1、登入流程

1)傳送驗證碼

使用者在提交手機號後,會校驗手機號是否合法,如果不合法,則要求使用者重新輸入手機號

如果手機號合法,後臺此時生成對應的驗證碼,同時將驗證碼進行儲存,然後再透過簡訊的方式將驗證碼傳送給使用者

2)簡訊驗證碼登入、註冊

使用者將驗證碼和手機號進行輸入,
後臺從session中拿到當前驗證碼,然後和使用者輸入的驗證碼進行校驗,
如果不一致,則無法透過校驗,
如果一致,則後臺根據手機號查詢使用者,
如果使用者不存在,則為使用者建立賬號資訊,儲存到資料庫。
無論是否存在,都會將使用者資訊儲存到session中,方便後續獲得當前登入資訊

2、實現驗證碼傳送

使用MyBatisX實現專案的初始化

1)正規表示式

分別對手機號、密碼、驗證碼進行校驗

正規表示式可以去網上找

public class RegexPatterns {
    /**
     * 手機號正則
     */
    public static final String PHONE_REGEX="1\\d{10}";
    /**
     * 郵箱正則
     */
    public static final String EMAIL_REGEX="/^([a-z0-9_\\.-]+)@([\\da-z\\.-]+)\\.([a-z\\.]{2,6})$/";
    /**
     * 驗證碼正則
     */
    public static final String VERIFY_CODE_REGEX="^[a-zA-Z\\d]{6}$";

}

2)正則校驗工具類

controller傳入的手機號進行校驗

滿足手機號正規表示式,手機號11位,並且只能為數字,才能校驗透過

官網:https://doc.hutool.cn/pages/index/#📚簡介

public class RegexUtils {

    /**
     * 校驗手機號是否合法
     * @param phone
     * @return
     */
    public static boolean isPhoneInvalid(String phone){
        boolean matches = phone.matches(RegexPatterns.PHONE_REGEX);
        return matches;
    }

    /**
     * 校驗驗證碼是否合法
     * @param code
     * @return
     */
    public boolean isCodeInvalid(String code){
        boolean matches = code.matches(RegexPatterns.VERIFY_CODE_REGEX);
        return matches;
    }
}

3)Controller層

@GetMapping("/code")
    public boolean SendCode(String phone, HttpSession session){
        boolean b = userService.sendCode(phone, session);
        return b;
    }

4)service層

首先對傳入的手機號放到正則校驗工具類中校驗

校驗成功後生成驗證碼

將驗證碼存入session中

將驗證碼在控制檯以debug形式輸出

@Resource
private UserService userService;

public boolean sendCode(String phone, HttpSession session) {
    //1、校驗手機號是否合法
    if (!RegexUtils.isPhoneInvalid(phone)) {
        return false;
    }
    //2、生成隨機驗證嗎
    String code = RandomUtil.randomNumbers(6);
    //3、儲存驗證碼
    session.setAttribute("code",code);
    //4、列印日誌
    log.debug("傳送簡訊驗證碼成功,驗證碼:{}",code);
    return true;
}

注意:這裡需要開啟debug日誌

controller層中加入@Slf4j註解

logging:
  level:
    com.example: debug
# 開啟debug日誌

結果:

3、實現驗證碼登入註冊

簡訊驗證登入註冊邏輯:

  • 校驗手機號
  • 校驗驗證碼
    • 取出session中儲存的驗證碼與表單中的輸入的驗證碼嗎進行比較
  • 不一致:報錯
  • 一致:根據手機號查詢使用者
  • 判斷使用者是否存在
  • 不存在,根據手機號建立新使用者並儲存
    • 使用者是憑空建立的,所以密碼可以沒有,
    • 使用者呢稱和頭像都是隨機預設的
  • 儲存使用者資訊到session中

1)Controller層

我們登入需要獲取兩個引數
使用者名稱和手機號,根據手機號來建立使用者名稱

@PostMapping("/login")
    public boolean login(LoginFormDTO loginFormDTO, HttpSession session){
        boolean login = userService.Login(loginFormDTO, session);
        return login;
    }

為了使程式碼更加美觀,建立一個引數封裝類

/**
 * 使用者登入請求引數封裝類
 */
@Data
public class LoginFormDTO {
    private String code;
    private String phone;
}

2)service層

/**
     * 使用者登入
     * @param loginFormDTO
     * @param session
     * @return
     */
    @Override
    public boolean Login(LoginFormDTO loginFormDTO, HttpSession session) {
        //1、首先校驗手機號和驗證碼是否合法
        String phone = loginFormDTO.getPhone();
        if(!RegexUtils.isPhoneInvalid(phone)){
            return false;
        }
        //2、校驗驗證碼
        Object cachecode = session.getAttribute("code");
        String dtoCode = loginFormDTO.getCode();
        if (dtoCode==null&&!dtoCode.equals(cachecode)) {
            return false;
        }
        //3、根據手機號查詢使用者資訊
        QueryWrapper<User> queryWrapper=new QueryWrapper<User>();
        queryWrapper.eq("phone", phone);
        //4、根據查詢條件查詢資料庫中滿足以上條件的使用者
        User user = userMapper.selectOne(queryWrapper);
        if (user==null) {
            //建立使用者
            user=CreateUser(phone);
        }
        //5、儲存使用者資訊到session中
        session.setAttribute("user",user);
        return true;
    }

將建立使用者的這段程式碼單獨封裝為一個函式

/**
     * 建立使用者
     * @param phone
     * @return
     */
    private User CreateUser(String phone){
        User user = new User();
        user.setPhone(phone);
        user.setNickName(RandomUtil.randomString(10));
        //儲存使用者
        save(user);
        return user;
    }

傳送驗證碼

get http://localhost:8080/api/user/code?phone=13177576913

校驗驗證碼並登入

post http://localhost:8080/api/user/login?phone=13177576913
&code=686422

結果:

4、最佳化一:全域性通用返回物件

前面我的測試前端返回的資料都太過單調,因為這是一個功能的實現,並不是做一個完整的專案,但這樣看起來確實不太美觀
所以這裡使用==全域性統一API響應框架==對整個介面進行統一的異常處理封裝,這樣就不需要寫那麼多的異常處理類了。

1)引入依賴

使用rest-api-spring-boot-starter這個依賴,不用開啟Redis,但這個依賴需要加上

<!--RestfulAPI-->
<dependency>
    <groupId>cn.soboys</groupId>
    <artifactId>rest-api-spring-boot-starter</artifactId>
    <version>1.3.0</version>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

2)註解

啟動類上加上這個註解
@EnableRestFullApi
就只需要這兩步
LOG日誌都不一樣了

注意:
這裡的埠號是8000,也就是說使用這個依賴必須要8000埠,我們在配置檔案中所
設定的web埠沒有用了,無法自定義埠
執行

我們先進行使用者脫敏

3)使用者資訊脫敏

將返回物件單獨封裝

@Data
public class UserDTO {
    private Long id;
    private String nickName;
    private String icon;
}

修改登入login方法中的

/**
     * copyProperties:屬性複製——把user中的屬性字動複製到UserDTO中
     * BeanUtils:使用的是包cn.hutool.core.bean下的工具類
     */
//5、儲存使用者資訊到session中
UserDTO userDTO = BeanUtil.copyProperties(user, UserDTO.class);
session.setAttribute("user", userDTO);

結果這裡就不說了,成功返回三個資訊

相關文章