1. 前置知識
1.1 MyBatisPlus的基本使用
1.1.1 引入依賴
<dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.4.3</version>
</dependency>
1.1.2 建立實體類和數據庫表
實體類:
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@TableName("tb_user")
public class User implements Serializable {private static final long serialVersionUID = 1L;/*** 主鍵*/@TableId(value = "id", type = IdType.AUTO)private Long id;/*** 手機號碼*/private String phone;/*** 密碼,加密存儲*/private String password;/*** 昵稱,默認是隨機字符*/private String nickName;/*** 用戶頭像*/private String icon = "";/*** 創建時間*/private LocalDateTime createTime;/*** 更新時間*/private LocalDateTime updateTime;}
數據庫表:
注解 | 所屬框架 | 用途說明 |
---|---|---|
@Data | Lombok | 自動生成 getter/setter/toString/equals/hashCode |
@EqualsAndHashCode(callSuper = false) | Lombok | 控制 equals 和 hashCode 是否包含父類字段 |
@Accessors(chain = true) | Lombok | 支持鏈式調用(set 方法返回 this) |
@TableName("tb_user") | MyBatis Plus | 指定當前類對應的數據庫表名 |
@TableId(value = "id", type = IdType.AUTO) | MyBatis Plus | 標明主鍵字段,并設置數據庫主鍵列名和類型 |
1.1.3 application.yml
mybatis-plus:type-aliases-package: com.hmdp.entity # 別名掃描包
1.1.4 啟動類掃描包
package com.hmdp;import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;@MapperScan("com.hmdp.mapper") // MyBatis掃描包
@SpringBootApplication
public class HmDianPingApplication {public static void main(String[] args) {SpringApplication.run(HmDianPingApplication.class, args);}
}
1.1.5 Mapper接口
import com.hmdp.entity.User;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;public interface UserMapper extends BaseMapper<User> {}
BaseMapper 是什么?
BaseMapper<T>
是 MyBatis Plus 提供的一個接口,包含一系列常用的 CRUD 操作方法(增刪改查),這些方法已經實現好了,你可以直接使用。
常用方法舉例:
方法名 | 功能說明 |
---|---|
int insert(User entity) | 插入一條記錄 |
int deleteById(Serializable id) | 根據主鍵刪除一條記錄 |
int updateById(User entity) | 根據主鍵更新記錄 |
User selectById(Serializable id) | 根據主鍵查詢記錄 |
List<User> selectList(QueryWrapper<User> wrapper) | 查詢滿足條件的多條記錄 |
List<User> selectAll() | 查詢所有記錄(需自己定義 SQL 或擴展 Wrapper) |
1.1.6 Service接口
package com.hmdp.service;import com.baomidou.mybatisplus.extension.service.IService;
import com.hmdp.entity.User;public interface IUserService extends IService<User> {}
IService 是什么?
IService<T>
是 MyBatis Plus 提供的一個 通用服務接口,封裝了常見的 業務層操作方法(CRUD),例如:
方法名 | 功能說明 |
---|---|
boolean save(User entity) | 插入一條記錄 |
boolean removeById(Serializable id) | 根據主鍵刪除記錄 |
boolean updateById(User entity) | 根據主鍵更新記錄 |
User getById(Serializable id) | 根據主鍵查詢記錄 |
List<User> list() | 查詢所有記錄 |
IPage<User> page(IPage<User> page) | 分頁查詢數據 |
boolean saveOrUpdate(User entity) | 存在則更新,不存在則插入 |
1.1.8 ServiceImpl實現類
package com.hmdp.service.impl;import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hmdp.entity.User;
import com.hmdp.mapper.UserMapper;
import com.hmdp.service.IUserService;
import org.springframework.stereotype.Service;@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {}
UserServiceImpl ?是?IUserService ?的實現類 | 繼承?ServiceImpl ,獲得通用 CRUD 方法 |
必須指定兩個泛型參數 | Mapper 和 Entity |
使用?@Service ?注解 | 讓 Spring 管理這個類 |
可以直接使用通用方法 | 如?save() ,?getById() ,?list() ?等 |
支持擴展自定義方法 | 在接口中聲明,在實現類中實現 |
1.1.9?Controller接口
@Slf4j
@RestController
@RequestMapping("/user")
public class UserController {@Resourceprivate IUserService userService;@Resourceprivate IUserInfoService userInfoService;/*** 發送手機驗證碼*/@PostMapping("code")public Result sendCode(@RequestParam("phone") String phone, HttpSession session) {// TODO 發送短信驗證碼并保存驗證碼return Result.fail("功能未完成");}/*** 登錄功能* @param loginForm 登錄參數,包含手機號、驗證碼;或者手機號、密碼*/@PostMapping("/login")public Result login(@RequestBody LoginFormDTO loginForm, HttpSession session){// TODO 實現登錄功能return Result.fail("功能未完成");}/*** 登出功能* @return 無*/@PostMapping("/logout")public Result logout(){// TODO 實現登出功能return Result.fail("功能未完成");}@GetMapping("/me")public Result me(){// TODO 獲取當前登錄的用戶并返回return Result.fail("功能未完成");}@GetMapping("/info/{id}")public Result info(@PathVariable("id") Long userId){// 查詢詳情UserInfo info = userInfoService.getById(userId);if (info == null) {// 沒有詳情,應該是第一次查看詳情return Result.ok();}info.setCreateTime(null);info.setUpdateTime(null);// 返回return Result.ok(info);}
}
1.2 校驗手機號/驗證碼格式的工具類
注意:要引入hutool依賴。
package com.hmdp.utils;public abstract class RegexPatterns {/*** 手機號正則*/public static final String PHONE_REGEX = "^1([38][0-9]|4[579]|5[0-3,5-9]|6[6]|7[0135678]|9[89])\\d{8}$";/*** 郵箱正則*/public static final String EMAIL_REGEX = "^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(\\.[a-zA-Z0-9_-]+)+$";/*** 密碼正則。4~32位的字母、數字、下劃線*/public static final String PASSWORD_REGEX = "^\\w{4,32}$";/*** 驗證碼正則, 6位數字或字母*/public static final String VERIFY_CODE_REGEX = "^[a-zA-Z\\d]{6}$";}
package com.hmdp.utils;import cn.hutool.core.util.StrUtil;public class RegexUtils {/*** 是否是無效手機格式* @param phone 要校驗的手機號* @return true:符合,false:不符合*/public static boolean isPhoneInvalid(String phone){return mismatch(phone, RegexPatterns.PHONE_REGEX);}/*** 是否是無效郵箱格式* @param email 要校驗的郵箱* @return true:符合,false:不符合*/public static boolean isEmailInvalid(String email){return mismatch(email, RegexPatterns.EMAIL_REGEX);}/*** 是否是無效驗證碼格式* @param code 要校驗的驗證碼* @return true:符合,false:不符合*/public static boolean isCodeInvalid(String code){return mismatch(code, RegexPatterns.VERIFY_CODE_REGEX);}// 校驗是否不符合正則格式private static boolean mismatch(String str, String regex){if (StrUtil.isBlank(str)) {return true;}return !str.matches(regex);}
}
2. 短信登陸
2.1 基于Session實現登陸
用戶第一次訪問服務器時,服務器內部會生成一個session,這個session屬于這個用戶,有一個sessionID,服務器會自動將sessionID傳遞給瀏覽器,瀏覽器會自動保存sessionID,之后瀏覽器的每一次請求都會自動攜帶這個sessionID
2.2.1 實現發送短信驗證碼功能
業務流程:
- 前端提交手機號到后端
- 后端校驗手機號
- 手機號格式不符合,返回手機號格式錯誤
- 手機號格式符合,下一步
- 生成驗證碼
- 保存驗證碼到當前用戶的session中
- 將驗證碼發送給前端
前端向后端發送請求的倆種方式(POST請求):
- http://192.168.31.20:8080/api/user/code?phone=17771818987(通過Nginx代理轉發)
- http://localhost:8081/code?phone=17771818987(直接訪問后端)
UserController.java
@Slf4j
@RestController
@RequestMapping("/user")
public class UserController {@Resourceprivate IUserService userService;/*** 發送手機驗證碼*/@PostMapping("code")public Result sendCode(@RequestParam("phone") String phone, HttpSession session) {// 發送短信驗證碼并保存驗證碼return userService.sendCode(phone,session);}}
IUserService.java
public interface IUserService extends IService<User> {/*** 登陸發送短信驗證碼接口* @param phone* @param session* @return*/Result sendCode(String phone, HttpSession session);
}
UserServiceImpl.java
@Service
@Slf4j
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {/*** 發送短信驗證碼實現* @param phone* @param session* @return*/@Overridepublic Result sendCode(String phone, HttpSession session) {// 1. 校驗手機號if (RegexUtils.isPhoneInvalid(phone)) {// 2. 不符合,返回錯誤信息return Result.fail("手機號格式錯誤");}// 3. 符合,設生成驗證碼String code = RandomUtil.randomNumbers(6);// 通過hutool包,生成6位隨機數// 4. 保存驗證碼到sessionsession.setAttribute("code", code);// 5. 發送驗證碼給前端log.debug("發送短信驗證碼成功, 短信驗證碼是:{}", code);// 返回OKreturn Result.ok();}
}
2.2.2 實現短信驗證碼登陸和注冊
前端參數:
- 請求方式:POST
- 請求路徑:/user/login
- 請求參數:phone, code(json數據)
- 返回值:無
業務流程:
- 前端傳遞phone和code
- 通過session對象校驗驗證碼
- 驗證碼錯誤,返回“驗證碼錯誤”
- 驗證碼正確,去數據庫根據手機號查詢用戶
- 用戶是否存在
- 用戶不存在,創建新用戶,保存新用戶的數據庫
- 用戶存在,保存用戶信息到session
LoginFormDTO對象:
@Data
public class LoginFormDTO {private String phone;private String code;private String password;
}
2.2.3 實現登陸校驗攔截器
業務流程:
- 請求并攜帶cookie
- 從session獲取用戶
- 判斷用戶是否存在session中
- 用戶存在,表示手機號和驗證碼已經核驗通過
- 用戶不存在,攔截
2.2 集群的session共享問題
2.3 基于Redis實現共享session登陸