欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Redis實現(xiàn)短信登錄的示例代碼

 更新時間:2023年07月04日 15:30:22   作者:Cimbala  
本文主要介紹了Redis實現(xiàn)短信登錄的示例代碼,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧

一、基于Session實現(xiàn)登錄

---------------------------------------------------Controller
@PostMapping("code")
public Result sendCode(@RequestParam("phone") String phone, HttpSession session) {
    //發(fā)送短信驗證碼并保存驗證碼
    return userService.sendCode(phone,session);
}
@PostMapping("/login")
public Result login(@RequestBody LoginFormDTO loginForm, HttpSession session){
    //實現(xiàn)登錄功能  loginForm 登錄參數(shù),包含手機號、驗證碼;或者手機號、密碼
    return userService.login(loginForm,session);
}
@GetMapping("/me")
public Result me(){
    // 獲取當前登錄的用戶并返回
    UserDTO user = UserHolder.getUser();
    return Result.ok(user);
}
---------------------------------------------------Service
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {
    @Override
    public Result sendCode(String phone, HttpSession session) {
        if(RegexUtils.isPhoneInvalid(phone)){
            return Result.fail("手機號格式錯誤");
        }
        String code = RandomUtil.randomNumbers(6);
        session.setAttribute("code",code);
        log.debug("短信驗證碼" + code);
        return Result.ok();
    }
    @Override
    public Result login(LoginFormDTO loginForm, HttpSession session) {
        if(RegexUtils.isPhoneInvalid(loginForm.getPhone())){
            return Result.fail("手機號格式錯誤");
        }
        Object catchCode = session.getAttribute("code");
        String code = loginForm.getCode();
        if(catchCode == null || !catchCode.equals(code)){
            Result.fail("驗證碼錯誤");
        }
        User user = query().eq("phone", loginForm.getPhone()).one();
        if (user == null) {
            user = createUserWithPhone(loginForm.getPhone());
        }
        session.setAttribute("user", BeanUtil.copyProperties(user, UserDTO.class));
        return Result.ok();
    }
    private User createUserWithPhone(String phone) {
        User user = new User();
        user.setPhone(phone);
        user.setNickName(USER_NICK_NAME_PREFIX + RandomUtil.randomNumbers(3));
        save(user);
        return user;
   	}
}
---------------------------------------------------自定義攔截器
public class LoginInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        HttpSession session = request.getSession();
        Object user = session.getAttribute("user");
        if (user == null) {
            response.setStatus(401);
            return false;
        }
        UserHolder.saveUser((UserDTO) user);
        return true;
    }
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        UserHolder.removeUser();
    }
}
---------------------------------------------------添加攔截器并指定攔截的請求和不攔截的請求
@Configuration
public class MvcConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
   		 //實現(xiàn)WebMvcConfigurer接口中的addInterceptors方法把自定義的攔截器類添加進來即可
        registry.addInterceptor(new LoginInterceptor()).excludePathPatterns(
                "/user/code",
                "/user/login",
                "/blog/hot",
                "/shop/**",
                "/shop-type/**",
                "/voucher/**"
        );
    }
}

總結: 短信驗證碼用隨機工具生成6位數(shù),保存了session中,在用戶使用手機號登錄時,獲取session中的驗證碼和請求參數(shù)中的驗證碼比對,一致則去庫里查該手機號的用戶是否存在,不存在則新建用戶,并把該用戶對象存在在session中。校驗登錄狀態(tài)是使用HandlerInterceptor攔截器實現(xiàn)的,在此之前需要配置攔截哪些請求,不攔截哪些請求,從客戶端的請求中獲取session信息,為空返回401狀態(tài),不為空則把用戶信息存儲在ThreadLocal中,在請求處理完之后銷毀用戶信息。

問題: 集群的session共享問題。多臺Tomcat并不共享session存儲空間,當請求切換到不同Tomcat服務時導致數(shù)據(jù)丟失的問題。早期的Tomcat提供session拷貝功能,但是并不能解決問題,問題有1.多臺Tomcat保存相同的數(shù)據(jù)信息,內(nèi)存空間浪費;2.拷貝需要時間,在延遲之內(nèi)有用戶訪問,多臺Tomcat依然存在數(shù)據(jù)不一致。

二、基于Redis實現(xiàn)共享Session實現(xiàn)登錄

-------------------只記錄有變化的--------------------------------Service
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {
    @Resource
    private StringRedisTemplate stringRedisTemplate;
    @Override
    public Result sendCode(String phone, HttpSession session) {
        if (RegexUtils.isPhoneInvalid(phone)) {
            return Result.fail("手機號格式錯誤");
        }
        String code = RandomUtil.randomNumbers(6);
        stringRedisTemplate.opsForValue().set(LOGIN_CODE_KEY + phone, code, LOGIN_CODE_TTL, TimeUnit.MINUTES);
        log.debug("短信驗證碼" + code);
        return Result.ok();
    }
    @Override
    public Result login(LoginFormDTO loginForm, HttpSession session) {
        String phone = loginForm.getPhone();
        if (RegexUtils.isPhoneInvalid(phone)) {
            return Result.fail("手機號格式錯誤");
        }
        String cacheCode = stringRedisTemplate.opsForValue().get(LOGIN_CODE_KEY + phone);
        String code = loginForm.getCode();
        if (cacheCode == null || !cacheCode.equals(code)) {
            return Result.fail("驗證碼錯誤");
        }
        User user = query().eq("phone", phone).one();
        if (user == null) {
            user = createUserWithPhone(phone);
        }
        String token = UUID.randomUUID().toString(true);
        UserDTO userDTO = BeanUtil.copyProperties(user, UserDTO.class);
        Map<String, Object> userMap = BeanUtil.beanToMap(userDTO, new HashMap<>(),CopyOptions.create()
                .setIgnoreNullValue(true).setFieldValueEditor((fieldName, fieldValue) -> fieldValue.toString()));
        stringRedisTemplate.opsForHash().putAll(LOGIN_USER_KEY + token, userMap);
        stringRedisTemplate.expire(LOGIN_USER_KEY + token, LOGIN_USER_TTL, TimeUnit.MINUTES);
        return Result.ok(token);
    }
}
---------------------------------------------------添加攔截器并指定攔截的請求和不攔截的請求
@Configuration
public class MvcConfig implements WebMvcConfigurer {
    @Resource
    private StringRedisTemplate stringRedisTemplate;
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new LoginInterceptor()).excludePathPatterns(
                "/user/code",
                "/user/login",
                "/blog/hot",
                "/shop/**",
                "/shop-type/**",
                "/voucher/**"
        ).order(1);
        registry.addInterceptor(new RefreshTokenInterceptor(stringRedisTemplate)).addPathPatterns("/**").order(0);
    }
}
---------------------------------------------------自定義攔截器
public class LoginInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        //判斷ThreadLocal中是否有用戶
        if(UserHolder.getUser() == null){
            response.setStatus(401);
            return false;
        }
        return true;
    }
}
public class RefreshTokenInterceptor implements HandlerInterceptor {
    private StringRedisTemplate stringRedisTemplate;
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String token = request.getHeader("authorization");
        if (StrUtil.isBlank(token)) {
            return true;
        }
        Map<Object, Object> userMap = stringRedisTemplate.opsForHash().entries(RedisConstants.LOGIN_USER_KEY + token);
        if (userMap.isEmpty()) {
            return true;
        }
        UserDTO userDTO = BeanUtil.fillBeanWithMap(userMap, new UserDTO(), false);
        //保存在ThreadLocal中
        UserHolder.saveUser(userDTO);
        //刷新token有效期
        stringRedisTemplate.expire(RedisConstants.LOGIN_USER_KEY + token,RedisConstants.LOGIN_USER_TTL, TimeUnit.MINUTES);
        return true;
    }
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        UserHolder.removeUser();
    }
    public RefreshTokenInterceptor(StringRedisTemplate stringRedisTemplate) {
        this.stringRedisTemplate = stringRedisTemplate;
    }
}

總結: 短信驗證碼修改為以手機號為key驗證碼為value保存在redis中,在用戶使用手機號登錄時,獲取redis中的驗證碼和請求參數(shù)中的驗證碼比對,一致則去庫里查該手機號的用戶是否存在,不存在則新建用戶,并把該用戶對象存在在redis中。校驗登錄狀態(tài)是使用HandlerInterceptor攔截器實現(xiàn)的,在此之前需要配置攔截哪些請求,不攔截哪些請求,從客戶端的請求頭中獲取token信息,并從redis中獲取用戶信息, 為空返回401狀態(tài),不為空則把用戶信息存儲在ThreadLocal中,還把驗證碼和用戶信息設置有效時間,時間一過,則退出用戶登錄。在請求處理完之后銷毀用戶信息。

改造的點:

  • 發(fā)送短信驗證碼時,key使用手機號來確保唯一存儲在redis中,在用戶登錄時可以根據(jù)key來取出驗證碼校對。
  • 短信驗證碼登錄時,key使用UUID來確保唯一存儲在redis中,為確保將來前端能把token發(fā)送過來進行校驗,在請求結束前把token返回給客戶端。

問題:

1.攔截器能否真正的實現(xiàn)只要用戶一直在訪問,token就不會過期?

不行,因為攔截器只攔截需要登錄的路徑,如果用戶在有效期內(nèi)一直訪問不需要登錄的路徑,那么redis中的token就會過期。問題解決如下圖。

 到此這篇關于Redis實現(xiàn)短信登錄的示例代碼的文章就介紹到這了,更多相關Redis 短信登錄內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • 使用redis生成唯一編號及原理示例詳解

    使用redis生成唯一編號及原理示例詳解

    今天介紹下如何使用redis生成唯一的序列號,其實主要思想還是利用redis單線程的特性,可以保證操作的原子性,使讀寫同一個key時不會出現(xiàn)不同的數(shù)據(jù),感興趣的朋友跟隨小編一起看看吧
    2021-09-09
  • windows上修改redis端口號的操作步驟

    windows上修改redis端口號的操作步驟

    redis是一個開源的內(nèi)存數(shù)據(jù)結構存儲系統(tǒng),常用做數(shù)據(jù)庫、緩存和消息代理,默認的端口號為6379,那么如何在windows上修改redis端口號,接下來本文給大家詳細介紹了windows上修改redis端口號的操作方法,需要的朋友可以參考下
    2024-02-02
  • Redis配合SSDB實現(xiàn)持久化存儲代碼示例

    Redis配合SSDB實現(xiàn)持久化存儲代碼示例

    這篇文章主要介紹了Redis配合SSDB實現(xiàn)持久化存儲代碼示例,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2020-11-11
  • Redis?整數(shù)集合的具體使用(intset)

    Redis?整數(shù)集合的具體使用(intset)

    對于集合,STL?的?set?相信大家都不陌生,本文主要介紹了整數(shù)集合,又稱為?intset,文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-02-02
  • Redis Redisson lock和tryLock的原理分析

    Redis Redisson lock和tryLock的原理分析

    這篇文章主要介紹了Redis Redisson lock和tryLock的原理分析,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-04-04
  • 詳解SSH框架和Redis的整合

    詳解SSH框架和Redis的整合

    本篇文章主要介紹了SSH框架和Redis的整合,詳細的介紹了Struts+Spring+Hibernate和Redis整合,有興趣的可以了解一下。
    2017-03-03
  • 詳解Redis中的雙鏈表結構

    詳解Redis中的雙鏈表結構

    這篇文章主要介紹了Redis中的雙鏈表結構,包括listNode結構的API,需要的朋友可以參考下
    2015-08-08
  • websocket+redis動態(tài)訂閱和動態(tài)取消訂閱的實現(xiàn)示例

    websocket+redis動態(tài)訂閱和動態(tài)取消訂閱的實現(xiàn)示例

    本文主要介紹了websocket+redis動態(tài)訂閱和動態(tài)取消訂閱,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2022-05-05
  • Redis?Key使用{}原因分析

    Redis?Key使用{}原因分析

    這篇文章主要為大家介紹了Redis中Key中為什么要使用{}原因分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-09-09
  • 詳解Redis緩存預熱的實現(xiàn)方法

    詳解Redis緩存預熱的實現(xiàn)方法

    緩存預熱是一種在程序啟動或緩存失效之后,主動將熱點數(shù)據(jù)加載到緩存中的策略,本文將給大家分享一下如何實現(xiàn)Redis的緩存預熱,文中有詳細的實現(xiàn)代碼,需要的朋友可以參考下
    2023-10-10

最新評論