網(wǎng)站首頁 編程語言 正文
一、基于Session實(shí)現(xiàn)登錄
1.1 業(yè)務(wù)流程圖
二、發(fā)送短信驗(yàn)證碼
2.1 發(fā)送短信請(qǐng)求方式及參數(shù)說明
這個(gè)地方為什么需要session?? 因?yàn)槲覀冃枰羊?yàn)證碼保存在session當(dāng)中
/**
* 發(fā)送手機(jī)驗(yàn)證碼
*/
@PostMapping("code")
public Result sendCode(@RequestParam("phone") String phone, HttpSession session) {
// TODO 發(fā)送短信驗(yàn)證碼并保存驗(yàn)證碼
// return Result.fail("功能未完成");
return userService.sendCode(phone,session);
}
2.2 業(yè)務(wù)層代碼模擬發(fā)送短信
@Override
public Result sendCode(String phone, HttpSession session) {
// 1.校驗(yàn)手機(jī)號(hào)
if(RegexUtils.isPhoneInvalid(phone)){
// 說明:RegexUtils使我們封裝的一個(gè)類 isCodeInvalid是里面的靜態(tài)方法,在這個(gè)靜態(tài)方法里面又調(diào)用了另外一個(gè)靜態(tài)方法得以實(shí)現(xiàn)
// 2.如果不符合,返回錯(cuò)誤信息
return Result.fail("手機(jī)號(hào)格式錯(cuò)誤");
}
// 3.符合,生成驗(yàn)證碼 6代表生成的驗(yàn)證碼的長(zhǎng)度 RandomUtil使用這個(gè)工具類生成
String code = RandomUtil.randomNumbers(6);
// 4.保存驗(yàn)證碼到session key必須是一個(gè)字符串,value是一個(gè)對(duì)象
session.setAttribute("code",code);
// 5.發(fā)送驗(yàn)證碼
// 實(shí)現(xiàn)起來比較麻煩 我們使用日志假裝發(fā)送
log.debug("發(fā)送短信驗(yàn)證碼成功,驗(yàn)證碼:"+code);
return Result.ok();
}
}
三、登錄功能 ?
3.1? 短信驗(yàn)證的請(qǐng)求方式及路徑
/**
* 登錄功能
* @param loginForm 登錄參數(shù),包含手機(jī)號(hào)、驗(yàn)證碼;或者手機(jī)號(hào)、密碼
*/
@PostMapping("/login")
public Result login(@RequestBody LoginFormDTO loginForm, HttpSession session){
// TODO 實(shí)現(xiàn)登錄功能
return userService.login(loginForm,session);
}
3.2? 業(yè)務(wù)層代碼實(shí)現(xiàn)用戶登錄
流程圖:
代碼:
/**
* 實(shí)現(xiàn)用戶登錄
* @param loginForm 登錄的參數(shù)
* @param session
* @return
*/
@Override
public Result login(LoginFormDTO loginForm, HttpSession session) {
// 1.校驗(yàn)手機(jī)號(hào)
if(RegexUtils.isPhoneInvalid(loginForm.getPhone())){
// 說明:RegexUtils使我們封裝的一個(gè)類 isCodeInvalid是里面的靜態(tài)方法,在這個(gè)靜態(tài)方法里面又調(diào)用了另外一個(gè)靜態(tài)方法得以實(shí)現(xiàn)
// 1.2.如果不符合,返回錯(cuò)誤信息
return Result.fail("手機(jī)號(hào)格式錯(cuò)誤");
}
// 2.校驗(yàn)驗(yàn)證碼
// 2.1 得到code 這個(gè)值是真實(shí)的code
Object cacheCode = session.getAttribute("code");
// 2.2 獲取用戶輸入的code
String code = loginForm.getCode();
if(cacheCode ==null || !cacheCode.toString().equals(code)){
// 3.不一致,報(bào)錯(cuò)
return Result.fail("驗(yàn)證碼錯(cuò)誤");
}
// 4.一致,根據(jù)手機(jī)號(hào)查詢用戶 .one()代表查詢一個(gè) list()代表著查詢多個(gè)
User user =query().eq("phone",loginForm.getPhone()).one();
// 5.判斷用戶是否存在
if(user ==null){
// 6.不存在,創(chuàng)建新用戶并保存
user = createUserWithPhone(loginForm.getPhone());
}
// 7.保存用戶信息到session中
session.setAttribute("user",user);
return Result.ok();
}
private User createUserWithPhone(String phone) {
// 1.創(chuàng)建用戶
User user = new User();
user.setPhone(phone);
// USER_NICK_NAME_PREFIX其實(shí)就是 "user_",這樣寫更有逼格
user.setNickName(USER_NICK_NAME_PREFIX+RandomUtil.randomString(10));
// 保存用戶
save(user);
return user;
}
3.3 攔截器——登錄驗(yàn)證功能
// HandlerInterceptor 這是一個(gè)攔截器
public class LoginInterceptor implements HandlerInterceptor {
// 前置攔截 在進(jìn)入controller之前我們進(jìn)行登錄校驗(yàn)
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 1.獲取session
HttpSession session =request.getSession();
// 2.獲取session中的用戶
Object user = session.getAttribute("user");
// 3.判斷用戶是否存在
if(user == null){
// 4.不存在,攔截
response.setStatus(401); //返回401狀態(tài)碼
return false;
}
// 5.存在,保存用戶信息到ThreadLocal 保存在當(dāng)前線程里面的
UserHolder.saveUser((User)user);
// 6.放行
return true;
}
// 在controller執(zhí)行之后攔截 這個(gè)我們?cè)谶@里不需要
// @Override
// public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
// HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
// }
// 渲染之后,返回給用戶之前 用戶業(yè)務(wù)執(zhí)行完畢我們要銷毀維護(hù)信息,避免泄露
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
// 移除用戶
UserHolder.removeUser();
}
}
public class UserHolder {
private static final ThreadLocal<User> tl = new ThreadLocal<>();
public static void saveUser(User user){
tl.set(user);
}
public static User getUser(){
return tl.get();
}
public static void removeUser(){
tl.remove();
}
}
@Configuration
public class MvcConfig implements WebMvcConfigurer {
// 攔截器的注冊(cè)器
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LoginInterceptor())
.excludePathPatterns(
"/user/code",
"/user/login",
"/shop/**",
"/blog/hot",
"/shop-type/**",
"upload/**",
"voucher/**"
);
}
}
@GetMapping("/me")
public Result me(){
// TODO 獲取當(dāng)前登錄的用戶并返回
// 直接取就可以了
User user= UserHolder.getUser();
return Result.ok(user);
}
三、隱藏用戶敏感信息
如下圖所示,服務(wù)器返回的信息有點(diǎn)多,我們?yōu)榱吮Wo(hù)用戶的信息,我們需要隱藏部分的內(nèi)容
所以一開始我們存入session的信息就不應(yīng)該是完整的信息,這樣才能降低服務(wù)器的壓力
UserServiceImpl中的login方法
// 7.保存用戶信息到session中 \
// BeanUtil.copyProperties(user, UserDTO.class)) 會(huì)自動(dòng)的將user中的屬性拷貝到UserDTO當(dāng)中而且也創(chuàng)建出一個(gè)UserDTO對(duì)象
session.setAttribute("user", BeanUtil.copyProperties(user, UserDTO.class));
取的時(shí)候我們也應(yīng)該做出變化
LoginInterceptor類
// 5.存在,保存用戶信息到ThreadLocal 保存在當(dāng)前線程里面的
UserHolder.saveUser((UserDTO)user);
此時(shí)我們?cè)俚卿洸樵冃畔ⅲ瓦€剩下三個(gè)字段了
四、session共享問題
多臺(tái)Tomcat并不共享session存儲(chǔ)空間,當(dāng)請(qǐng)求切換到不同的Tomcat服務(wù)導(dǎo)致數(shù)據(jù)丟失的問題
所以這個(gè)方案就被pass了
session的替代方案應(yīng)該滿足:
數(shù)據(jù)共享內(nèi)存存儲(chǔ)key、value結(jié)構(gòu)
所以我們選擇Redis
任何一臺(tái)Tomcat都能訪問到Redis,這樣就能實(shí)現(xiàn)數(shù)據(jù)共享
總結(jié)
原文鏈接:https://blog.csdn.net/weixin_51351637/article/details/127519489
相關(guān)推薦
- 2023-01-03 C++定義和初始化string對(duì)象實(shí)例詳解_C 語言
- 2022-11-20 TS?中的類型推斷與放寬實(shí)例詳解_其它
- 2022-04-03 Pandas搭配lambda組合使用詳解_python
- 2024-04-05 idea使用docker生成鏡像(打包鏡像,導(dǎo)入鏡像,導(dǎo)出鏡像)
- 2022-06-08 淺析C++可變參數(shù)模板的展開方式_C 語言
- 2021-12-08 el-cascader 級(jí)聯(lián)選擇器回顯數(shù)據(jù) (帶有子級(jí)children)
- 2022-01-17 什么是token token用在哪 token放在哪比較好
- 2021-12-12 Redis實(shí)現(xiàn)分布式鎖的實(shí)例講解_Redis
- 最近更新
-
- window11 系統(tǒng)安裝 yarn
- 超詳細(xì)win安裝深度學(xué)習(xí)環(huán)境2025年最新版(
- Linux 中運(yùn)行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲(chǔ)小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎(chǔ)操作-- 運(yùn)算符,流程控制 Flo
- 1. Int 和Integer 的區(qū)別,Jav
- spring @retryable不生效的一種
- Spring Security之認(rèn)證信息的處理
- Spring Security之認(rèn)證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權(quán)
- redisson分布式鎖中waittime的設(shè)
- maven:解決release錯(cuò)誤:Artif
- restTemplate使用總結(jié)
- Spring Security之安全異常處理
- MybatisPlus優(yōu)雅實(shí)現(xiàn)加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務(wù)發(fā)現(xiàn)-Nac
- Spring Security之基于HttpR
- Redis 底層數(shù)據(jù)結(jié)構(gòu)-簡(jiǎn)單動(dòng)態(tài)字符串(SD
- arthas操作spring被代理目標(biāo)對(duì)象命令
- Spring中的單例模式應(yīng)用詳解
- 聊聊消息隊(duì)列,發(fā)送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠(yuǎn)程分支