最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

SpringBoot基于redis實(shí)現(xiàn)登錄校驗(yàn)的示例

 更新時(shí)間:2026年03月10日 09:38:57   作者:漫霂  
本文介紹SpringBoot項(xiàng)目基于手機(jī)驗(yàn)證碼的登錄實(shí)現(xiàn),包括驗(yàn)證碼發(fā)送、Redis存儲(chǔ)、用戶登錄與創(chuàng)建、攔截器校驗(yàn)Token及ThreadLocal數(shù)據(jù)共享,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧

實(shí)現(xiàn)流程

  1. 發(fā)送短信驗(yàn)證碼,并將驗(yàn)證碼保存在redis。

  2. 根據(jù)用戶登錄數(shù)據(jù)完成登錄。

    1. 如果是新用戶還要?jiǎng)?chuàng)建用戶。
    2. 如果查詢到則轉(zhuǎn)化為DTO保存。
      將用戶數(shù)據(jù)被保存到redis,并將token保存到redis,同時(shí)返回給前端。
  3. 創(chuàng)建登錄校驗(yàn)攔截器。
    注冊登錄校驗(yàn)攔截器。

發(fā)送驗(yàn)證碼接口

請求參數(shù):手機(jī)號phone
無結(jié)果對象

service層實(shí)現(xiàn)

@Override  
public Result sendCode(String phone) {  
    //1.校驗(yàn)手機(jī)號  
    if (RegexUtils.isPhoneInvalid(phone)) {  
        return Result.fail("手機(jī)號格式錯(cuò)誤!");  
    }  
    //2.生成驗(yàn)證碼  
    String code = RandomUtil.randomNumbers(6);  
    //3.將驗(yàn)證碼保存在redis  
    stringRedisTemplate.opsForValue().set(LOGIN_CODE_KEY + phone, code, LOGIN_CODE_TTL, TimeUnit.MINUTES);  
    //4.模擬發(fā)送驗(yàn)證碼  
    log.debug("發(fā)送短信驗(yàn)證碼成功,驗(yàn)證碼:{}", code);  
    return Result.ok();  
}

接口介紹

  1. 根據(jù)正則表達(dá)式檢驗(yàn)手機(jī)號格式。
  2. 通過hutool生成6位隨機(jī)數(shù)作為驗(yàn)證碼。
  3. 將驗(yàn)證碼保存在redis中。
  4. 將驗(yàn)證碼發(fā)送給客戶端(此處應(yīng)使用相關(guān)云服務(wù)完成,筆者使用日志只為記錄數(shù)據(jù))。

登錄接口

請求參數(shù):已經(jīng)封裝好的LoginFormDTO對象
結(jié)果對象:token

service層實(shí)現(xiàn)

@Override  
public Result login(LoginFormDTO loginForm) {  
    //1.校驗(yàn)手機(jī)號格式  
    String phone = loginForm.getPhone();  
    if (RegexUtils.isPhoneInvalid(phone)) {  
        return Result.fail("手機(jī)號格式錯(cuò)誤!");  
    }  
    //2.校驗(yàn)驗(yàn)證碼  
    String cacheCode = stringRedisTemplate.opsForValue().get(LOGIN_CODE_KEY + phone);  
    String code = loginForm.getCode();  
    if (cacheCode == null || !cacheCode.equals(code)) {  
        return Result.fail("驗(yàn)證碼錯(cuò)誤!");  
    }  
    //3.根據(jù)手機(jī)號查詢用戶  
    User user = query().eq("phone", phone).one();  
    //4.判斷用戶是否存在  
    if (user == null) {  
        //5.沒有用戶則創(chuàng)建新用戶  
        user = createUserWithPhone(phone);  
    }  
    //6.保存用戶信息到redis,(以哈希儲(chǔ)存)  
        //1.通過UUID拼裝tokenKey  
    String token = UUID.randomUUID().toString(true);  
    String tokenKey = LOGIN_USER_KEY + token;  
        //2.將UserDTO對象轉(zhuǎn)為HashMap存儲(chǔ), 由于使用的是stringRedisTemplate,要保證所有的值都是字符串類型,要把lang轉(zhuǎn)換為string  
    UserDTO userDTO = BeanUtil.copyProperties(user, UserDTO.class);  
  
    Map<String, Object> userMap = BeanUtil.beanToMap(userDTO, new HashMap<>(),  
            CopyOptions  
                    .create()//創(chuàng)建默認(rèn)數(shù)據(jù)拷貝選項(xiàng)  
                    .setIgnoreNullValue(true) //忽略null值  
                    /*字段值修改器,接收實(shí)現(xiàn)修改的方法*/  
                    .setFieldValueEditor(  
                            (fieldName, fieldValue) -> fieldValue.toString()  
                    )  
            );  
  
    stringRedisTemplate.opsForHash().putAll(tokenKey, userMap);  
        //3.設(shè)置token的有效期  
    stringRedisTemplate.expire(tokenKey, LOGIN_USER_TTL, TimeUnit.SECONDS);  
    //7.返回token  
    return Result.ok(tokenKey);  
}

接口介紹

  1. 校驗(yàn)手機(jī)號格式。
  2. 校驗(yàn)驗(yàn)證碼。
  3. 根據(jù)手機(jī)號查詢用戶信息(使用MybatisPlus)。
  4. 通過UUID生成隨機(jī)token,所為key,將查詢后得到的UserDTO(將User轉(zhuǎn)換為UserDTO,避免暴漏敏感數(shù)據(jù)),封裝為hash(使用hutool相關(guān)API),存入redis,并設(shè)置有效期。
  5. 返回token。

手動(dòng)創(chuàng)建攔截器

在實(shí)現(xiàn)了HandlerInterceptor的類中重寫preHandle和afterCompletion來實(shí)現(xiàn)對每一次請求的處理。

public class LoginInInterceptor implements HandlerInterceptor {  
  
    private StringRedisTemplate stringRedisTemplate;  
  
    public LoginInInterceptor(StringRedisTemplate stringRedisTemplate) {  
        this.stringRedisTemplate = stringRedisTemplate;  
    }  
      
    @Override  
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {  
  
        //獲取請求頭中的token  
        String token = request.getHeader("authorization");  
        if (token == null) {  
            response.setStatus(401);  
            return false;  
        }  
        //根據(jù)token從redis中獲取用戶信息  
        String key = LOGIN_USER_KEY + token;  
        Map<Object, Object> userMap = stringRedisTemplate.opsForHash().entries(key);  
        if (userMap.isEmpty()) {  
            response.setStatus(401);  
            return false;  
        }  
  
        //將map轉(zhuǎn)換為UserDTO對象  
        UserDTO userDTO = BeanUtil.fillBeanWithMap(userMap, new UserDTO(), false);  
  
        //保存用戶信息到ThreadLocal  
        UserHolder.saveUser(userDTO);  
        //刷新token有效期  
        stringRedisTemplate.expire(key, LOGIN_USER_TTL, TimeUnit.SECONDS);  
        return true;  
    }  
      
    @Override  
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {  
        UserHolder.removeUser();  
    }  
}

用前注意:
該自定義攔截器未交給Spring容器管理,不能使用@Autowired或者@Resource自動(dòng)注入依賴,此處使用構(gòu)造器注入stringRedisTemplate。

執(zhí)行邏輯:
preHandle執(zhí)行攔截校驗(yàn)

  1. getHeader獲取請求頭中的token,并進(jìn)行非空判斷。
  2. 根據(jù)token從redis中獲取userMap,再次使用hutool包的API將map集合轉(zhuǎn)換為實(shí)體類。
  3. 使用工具類將userDTO保存到ThreadLocal,實(shí)現(xiàn)跨層級數(shù)據(jù)共享。
  4. 刷新token有效期,提升用戶體驗(yàn)。
    afterCompletion執(zhí)行清理ThreadLocal
    請求完成后清理ThreadLocal中的數(shù)據(jù),防止內(nèi)存泄漏和數(shù)據(jù)污染。

注冊攔截器

在含有@Configuration 且實(shí)現(xiàn)了WebMvcConfigurer 的配置類中添加自定義攔截器

@Configuration  
public class MvcConfig implements WebMvcConfigurer {  
  
    @Resource  
    private StringRedisTemplate stringRedisTemplate;  
  
    @Override  
    public void addInterceptors(InterceptorRegistry registry) {  
        registry.addInterceptor(new LoginInInterceptor(stringRedisTemplate))  
                .excludePathPatterns(  
                        "/user/code",  
                        "/user/login",  
                        "/blog/hot",  
                        "/shop/**",  
                        "/shop-type/**",  
                        "/voucher/**",  
                        "/upload/**"  
                );  
    }  
}

執(zhí)行邏輯:

  1. 使用@Resource注解注入stringRedisTemplate
  2. 重寫addInterceptors方法
  3. 調(diào)用registry的addInterceptor方法添加攔截器(形參中new出LoginInInterceptor,并將正確注入的stringRedisTemplate傳遞給LoginInInterceptor,完成自定義攔截器中的依賴注入)。
  4. 調(diào)用excludePathPatterns指明不攔截的請求

攔截器的優(yōu)化

由于將校驗(yàn)登錄狀態(tài)和刷新token有效期合并在了一個(gè)攔截器中,所以在用戶訪問一些不攔截登錄的接口時(shí)無法刷新token有效期,為此,我們將它拆分為兩個(gè)攔截器

刷新攔截器:

只為刷新token有效期

  • 如果無token直接放行讓用戶進(jìn)行登錄
  • 如果有token則將用戶保存在ThreadLoacl(便于攔截登錄)并刷新token有效期
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 {  
  
        //獲取請求頭中的token  
        String token = request.getHeader("authorization");  
        if (token == null) {  
            return true;  
        }  
        //根據(jù)token從redis中獲取用戶信息  
        String key = token;  
        Map<Object, Object> userMap = stringRedisTemplate.opsForHash().entries(key);  
        if (userMap.isEmpty()) {  
            return true;  
        }  
  
        //將map轉(zhuǎn)換為UserDTO對象  
        UserDTO userDTO = BeanUtil.fillBeanWithMap(userMap, new UserDTO(), false);  
  
        //保存用戶信息到ThreadLocal  
        UserHolder.saveUser(userDTO);  
        //刷新token有效期  
        stringRedisTemplate.expire(key, LOGIN_USER_TTL, TimeUnit.SECONDS);  
        return true;  
    }  
  
    @Override  
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {  
        UserHolder.removeUser();  
    }  
}

登錄攔截器:

只為攔截未登錄用戶

public class LoginInInterceptor implements HandlerInterceptor {  
  
    @Override  
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {  
        //判斷是否需要攔截(ThreadLocal中是否有用戶)  
        if (UserHolder.getUser() == null) {  
            //無用戶,進(jìn)行攔截  
            response.setStatus(401);  
            return false;  
        }  
        //有用戶,放行  
        return true;  
    }  
  
    @Override  
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {  
        UserHolder.removeUser();  
    }  
  
}

配置攔截順序

@Configuration  
public class MvcConfig implements WebMvcConfigurer {  
  
    @Resource  
    private StringRedisTemplate stringRedisTemplate;  
  
    @Override  
    public void addInterceptors(InterceptorRegistry registry) {  
        registry.addInterceptor(new LoginInInterceptor())  
                .excludePathPatterns(  
                        "/user/code",  
                        "/user/login",  
                        "/blog/hot",  
                        "/shop/**",  
                        "/shop-type/**",  
                        "/voucher/**",  
                        "/upload/**"  
                ).order(1);  
            registry.addInterceptor(new ReFreshTokenInterceptor(stringRedisTemplate)).order(0);  
    }  
}

order值低的優(yōu)先進(jìn)行攔截

到此這篇關(guān)于SpringBoot基于redis實(shí)現(xiàn)登錄校驗(yàn)的示例的文章就介紹到這了,更多相關(guān)SpringBoot redis登錄校驗(yàn)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 淺談JAVA 內(nèi)存流的實(shí)現(xiàn)

    淺談JAVA 內(nèi)存流的實(shí)現(xiàn)

    這篇文章主要介紹了淺談JAVA 內(nèi)存流的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-02-02
  • Java編程接口回調(diào)一般用法代碼解析

    Java編程接口回調(diào)一般用法代碼解析

    本文的主要內(nèi)容是同過實(shí)際代碼向大家展示Java編程中接口回調(diào)的一般用法,具有一定參考價(jià)值,需要的朋友可以了解下
    2017-09-09
  • java面試常見問題之Hibernate總結(jié)

    java面試常見問題之Hibernate總結(jié)

    這篇文章主要介紹了在java面試過程中hibernate比較常見的問題,包括Hibernate的檢索方式,Hibernate中對象的狀態(tài),Hibernate的3種檢索策略是什么,Session的find()方法以及Query接口的區(qū)別等方面問題的總結(jié),需要的朋友可以參考下
    2015-07-07
  • java實(shí)現(xiàn)數(shù)字轉(zhuǎn)大寫的方法

    java實(shí)現(xiàn)數(shù)字轉(zhuǎn)大寫的方法

    這篇文章主要介紹了 java實(shí)現(xiàn)數(shù)字轉(zhuǎn)大寫的方法的相關(guān)資料,希望通過本文能幫助到大家,讓大家實(shí)現(xiàn)這樣的功能,需要的朋友可以參考下
    2017-10-10
  • Java如何將String轉(zhuǎn)換成json對象或json數(shù)組

    Java如何將String轉(zhuǎn)換成json對象或json數(shù)組

    這篇文章主要介紹了Java如何將String轉(zhuǎn)換成json對象或json數(shù)組,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-02-02
  • Intellij?IDEA創(chuàng)建web項(xiàng)目的超詳細(xì)步驟記錄

    Intellij?IDEA創(chuàng)建web項(xiàng)目的超詳細(xì)步驟記錄

    如果剛開始接觸IDEA,或者之前使用的是eclipse/myEclipse的話,即使是創(chuàng)建一個(gè)JAVA WEB項(xiàng)目,估計(jì)也讓很多人費(fèi)了好幾個(gè)小時(shí),下面這篇文章主要給大家介紹了關(guān)于Intellij?IDEA創(chuàng)建web項(xiàng)目的超詳細(xì)步驟,需要的朋友可以參考下
    2022-08-08
  • 淺析java消息摘要與數(shù)字簽名

    淺析java消息摘要與數(shù)字簽名

    這篇文章給大家分析了關(guān)于java消息摘要與數(shù)字簽名的相關(guān)知識(shí)點(diǎn)內(nèi)容,有興趣的朋友們可以學(xué)習(xí)下。
    2018-08-08
  • ElasticSearch學(xué)習(xí)之Es集群Api操作示例

    ElasticSearch學(xué)習(xí)之Es集群Api操作示例

    這篇文章主要為大家介紹了ElasticSearch學(xué)習(xí)之Es集群Api操作示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-01-01
  • tomcat報(bào)錯(cuò):Wrapper cannot find servlet class ...問題解決

    tomcat報(bào)錯(cuò):Wrapper cannot find servlet class ...問題解決

    這篇文章主要介紹了tomcat報(bào)錯(cuò):Wrapper cannot find servlet class ...問題解決的相關(guān)資料,需要的朋友可以參考下
    2016-11-11
  • 注入jar包里的對象,用@autowired的實(shí)例

    注入jar包里的對象,用@autowired的實(shí)例

    這篇文章主要介紹了注入jar包里的對象,用@autowired的實(shí)例,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-09-09

最新評論

科技| 合江县| 汤阴县| 开原市| 延边| 丹东市| 高青县| 龙口市| 康平县| 杨浦区| 谢通门县| 郴州市| 徐闻县| 南昌市| 夏邑县| 崇义县| 冕宁县| 梓潼县| 磴口县| 古丈县| 儋州市| 南皮县| 纳雍县| 广平县| 濮阳县| 上蔡县| 益阳市| 旺苍县| 获嘉县| 舒城县| 金湖县| 淄博市| 罗源县| 家居| 轮台县| 灵寿县| 浮山县| 九龙城区| 岳普湖县| 邵阳县| 阿合奇县|