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

Redis實現(xiàn)登錄注冊的示例代碼

 更新時間:2022年06月09日 10:39:59   作者:..Serendipity  
本文主要介紹了Redis實現(xiàn)登錄注冊的示例代碼,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧

1. 引言

在傳統(tǒng)的項目中,用戶登錄成功,將用戶信息保存在session中,這種方式在微服務架構中會產(chǎn)生一系列問題。例如在購物車服務具有多臺服務器,當一個請求落在購物車1號服務器后,其session保存了用戶信息,另一個請求落在了購物車2號服務器,發(fā)現(xiàn)沒有用戶信息,則重新需要進行登錄。服務器之間有session不共享的問題。為了解決這一問題,tomcat提出了內(nèi)存拷貝,即只需要配置一些信息即可實現(xiàn)多臺服務器之間的session拷貝,但是這種解決方案也有缺陷,例如:

  • 浪費空間
  • 拷貝有延時,如果在延時內(nèi)有請求訪問,則還會出現(xiàn)上述問題

為了解決此類問題,我們需要使用多個服務共享的信息平臺,例如Redis

2. 流程圖及代碼實現(xiàn)

直接上流程圖

在這里插入圖片描述

流程圖簡潔明了,其中需要注意的是

Redis中存入驗證碼的key是手機號拼接的字符串,為什么保存用戶到Redis的key要使用隨機token,而不是手機號拼接的字符串呢?

因為在用戶登錄注冊時,服務器會獲取到手機號,所以可以使用手機號作為key,進行驗證手機號和驗證碼時也方便進行匹對,那么在保存用戶信息到Redis時為什么要使用隨機token呢?因為在用戶獨立成功后,用戶的每次請求都會攜帶cookie,如果將保存用戶信息的key設置為含手機號的,那么用戶的請求中的cookie也需要攜帶手機號,這樣就會有一定的安全風險,所以在用戶登錄成功后,我們隨機生成token,用token作為key,并且返回給前端token,這樣前端請求時就會攜帶token,也避免了安全隱患。

2.1 生成驗證碼保存到Redis

   @Override
    public Result sedCode(String phone, HttpSession session) {
        //1. 校驗手機號
        if (RegexUtils.isPhoneInvalid(phone)) {
            //2.如果不符合,返回錯誤信息
            return Result.fail("手機號格式錯誤");
        }
        // 3.從redis里獲取驗證碼是否存在
        if(null==stringRedisTemplate.opsForValue().get("loginCode" + phone)){
            log.info("請勿重復獲取驗證碼");
            return Result.fail("請勿重復獲取驗證碼");
        }
        //4. 符合,生成驗證碼
        String code = RandomUtil.randomNumbers(6);
        //5. 保存驗證碼到redis,并設置有效期1分鐘,在設置key的時候,可以提前設置一個常量,然后在這里引用即可
        stringRedisTemplate.opsForValue().set("loginCode:"+phone,code,1, TimeUnit.MINUTES);

        //5. 發(fā)送驗證碼 模擬發(fā)送
        log.debug("發(fā)送短信驗證碼成功,驗證碼:{}",code);
        //返回ok
        return Result.ok();
    }

2.2 登錄驗證

@Override
    public Result login(LoginFormDTO loginForm, HttpSession session) {
        //1. 校驗手機號
        String phone = loginForm.getPhone();
        if (RegexUtils.isPhoneInvalid(phone)) {
            return Result.fail("手機號格式錯誤");
        }
        //2. 獲取Redis中的校驗驗證碼
        String cacheCode = stringRedisTemplate.opsForValue().get("loginCode" + phone);

        // 3.獲取表單中的驗證碼
        String code = loginForm.getCode();
        if (cacheCode == null || !cacheCode.toString().equals(code)){
            //3. 不一致,報錯
            return Result.fail("驗證碼錯誤");
        }

        //4.一致,根據(jù)手機號查詢用戶
        User user = query().eq("phone", phone).one();

        //5. 判斷用戶是否存在
        if (user == null){
            //6. 不存在,創(chuàng)建新用戶
            user = createUserWithPhone(phone);
        }

        //7.保存用戶信息到session
        // 生成token
        String token = UUID.randomUUID().toString();
        // 將User轉為Map
        UserDTO userDTO = BeanUtil.copyProperties(user, UserDTO.class);
        Map<String, Object> userDtoMap = BeanUtil.beanToMap(userDTO);
        // 存儲
        stringRedisTemplate.opsForHash().putAll("login:token:"+token,userDtoMap);
        // 設置有效期30分鐘
        stringRedisTemplate.expire("login:token:"+token,30, TimeUnit.MINUTES);
        // 返回token給前端
        return Result.ok(token);
    }

2.3 請求攔截器

有些請求是需要用戶登錄才能進行訪問的,所以我們設置一個登錄攔截器先攔截請求,判斷用戶是否登錄,如果登錄了就進行放行即可。

在這里插入圖片描述

2.3.1 實現(xiàn)HandlerInterceptor類

public class RefreshTokenInterceptor implements HandlerInterceptor {

    private StringRedisTemplate stringRedisTemplate;

    public RefreshTokenInterceptor(StringRedisTemplate stringRedisTemplate) {
        this.stringRedisTemplate = stringRedisTemplate;
    }

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 1.獲取請求頭中的token
        String token = request.getHeader("authorization");
        if (StrUtil.isBlank(token)) {
            return true;
        }
        // 2.基于TOKEN獲取redis中的用戶
        String key  = "login:token:" + token;
        Map<Object, Object> userMap = stringRedisTemplate.opsForHash().entries(key);
        // 3.判斷用戶是否存在
        if (userMap.isEmpty()) {
            return true;
        }
        // 5.將查詢到的hash數(shù)據(jù)轉為UserDTO
        UserDTO userDTO = BeanUtil.fillBeanWithMap(userMap, new UserDTO(), false);
        // 6.存在,保存用戶信息到 ThreadLocal
        UserHolder.saveUser(userDTO);
        // 7.刷新token有效期
        stringRedisTemplate.expire(key, LOGIN_USER_TTL, TimeUnit.MINUTES);
        // 8.放行
        return true;
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        // 移除用戶
        UserHolder.removeUser();
    }
}

preHandle方法是在controller之前運行,在這個方法里面可以進行驗證登錄狀態(tài)的操作。

  • 為什么要將用戶保存到ThreadLocal?因為每一個線程都是獨立的,如果將用戶信息保存到公共變量中,會造成線程安全問題,每一個線程都具備一個ThreadLocal內(nèi)存,我們將用戶信息保存到ThreadLocal中即可實現(xiàn)線程獨享一份用戶信息
  • 為什么要刷新Redis中用戶信息的有效時長?因為在session中,其機制是當用戶不在使用session中的數(shù)據(jù)超過30分鐘就會剔除session的數(shù)據(jù),所以在攔截器的前置攔截中進行刷新即可
  • 上述代碼的第三步驟,為什么userMap為空了還要放行呢?因為這個攔截器只是做Redis用戶信息刷新存活時間的功能,真正攔截的是LoginInterceptor,LoginInterceptor代碼在下面展示
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;
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        //移除用戶
        UserHolder.removeUser();
    }
}

兩個攔截器配置了,但是沒有生效,需要在配置類里進行配置

@Configuration
public class MvcConfig implements WebMvcConfigurer {
    @Autowired
    private StringRedisTemplate redisTemplate;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {


        // 登錄攔截器
        registry.addInterceptor(new LoginInterceptor())
                .excludePathPatterns(
                        // 配置不需要被攔截的路徑
                        "/shop/**",
                        "/voucher/**",
                        "/shop-type/**",
                        "/upload/**",
                        "/blog/hot",
                        "/user/code",
                        "/user/login"
                ).order(1);

        registry.addInterceptor(new RefreshTokenInterceptor(redisTemplate))
                .excludePathPatterns(
                        "/user/login",
                        "/user/code"
                ).order(0);
    }
}

這里配置兩個攔截器,兩個攔截器是有先后順序的,上述已經(jīng)說明,通過設置order屬性即可配置先后順序,值越小,優(yōu)先級越高。

3. 總結

用戶獲取驗證碼存放到redis

登錄請求拿著驗證碼和手機號去進行匹配,匹配成功后將用戶信息存入redis

需要登錄的請求會訪問攔截器,兩個攔截器,第一個攔截器RefreshTokenInterceptor負責刷新redis中用戶信息的TTL,并且如果Redis中有用戶信息,將存入ThreadLocal,第二個攔截器LoginInterceptor 用于檢測ThreadLocal是否具有用戶信息,如果沒有,則前往登錄界面,如果有就放行

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

相關文章

  • YII2框架手動安裝Redis擴展的過程

    YII2框架手動安裝Redis擴展的過程

    這篇文章主要介紹了YII2框架手動安裝Redis擴展的過程,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2022-06-06
  • redis 解決key的亂碼問題,并清理詳解

    redis 解決key的亂碼問題,并清理詳解

    這篇文章主要介紹了redis 解決key的亂碼問題,并清理詳解,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-07-07
  • 詳解利用redis + lua解決搶紅包高并發(fā)的問題

    詳解利用redis + lua解決搶紅包高并發(fā)的問題

    本篇文章主要介紹了利用redis + lua解決搶紅包高并發(fā)的問題 ,詳細的講訴了需求分析和方案,有興趣的可以了解一下。
    2016-11-11
  • 從零搭建SpringBoot2.X整合Redis框架的詳細教程

    從零搭建SpringBoot2.X整合Redis框架的詳細教程

    這篇文章主要介紹了從零搭建SpringBoot2.X整合Redis框架的詳細教程,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-12-12
  • Redis的4種緩存模式分享

    Redis的4種緩存模式分享

    這篇文章主要介紹了Redis的4種緩存模式分享,文章圍繞主題展開詳細的內(nèi)容介紹,具有一定的參考價值,感興趣的小伙伴可以參考一下
    2022-07-07
  • 淺談redis整數(shù)集為什么不能降級

    淺談redis整數(shù)集為什么不能降級

    本文主要介紹了redis整數(shù)集為什么不能降級,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2021-07-07
  • 詳解redis中的鎖以及使用場景

    詳解redis中的鎖以及使用場景

    這篇文章主要介紹了詳解redis中的鎖以及使用場景,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2020-12-12
  • 深入理解redis_memcached失效原理(小結)

    深入理解redis_memcached失效原理(小結)

    這篇文章主要介紹了深入理解redis_memcached失效原理(小結),小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-08-08
  • Redis+Hbase+RocketMQ?實際使用問題案例講解

    Redis+Hbase+RocketMQ?實際使用問題案例講解

    這篇文章主要介紹了Redis+Hbase+RocketMQ?實際使用問題案例分享,本文結合示例代碼給大家講解的非常詳細,需要的朋友可以參考下
    2023-01-01
  • Redis中的動態(tài)字符串學習教程

    Redis中的動態(tài)字符串學習教程

    這篇文章主要介紹了Redis中的動態(tài)字符串學習教程,以sds模塊的使用為主進行講解,需要的朋友可以參考下
    2015-08-08

最新評論