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

SpringBoot基于Redis實現(xiàn)短信登錄的操作

 更新時間:2023年12月12日 15:36:59   作者:在下小吉.  
驗證碼登錄是非常常見的一種登錄方式,能夠簡化用戶登錄的過程,本文主要介紹了SpringBoot基于Redis實現(xiàn)短信登錄的操作,具有一定的參考價值,感興趣的可以了解一下

前言

使用 Redis 進行登錄適用于以下情況:

  • 分布式系統(tǒng):
    當系統(tǒng)需要支持多個節(jié)點的分布式部署時,使用 Redis 存儲登錄信息能夠更好地支持多節(jié)點間的共享和同步,確保用戶的登錄狀態(tài)能夠在整個系統(tǒng)中得到有效的傳遞和管理。
  • 高并發(fā)訪問:
    面對大規(guī)模的并發(fā)訪問,使用 Redis 可以提供更好的性能表現(xiàn)。Redis 是一個基于內(nèi)存的高性能 Key-Value 數(shù)據(jù)庫,能夠更快速地讀取和寫入數(shù)據(jù),因此適用于需要處理大量并發(fā)請求的場景。
  • 靈活的數(shù)據(jù)結(jié)構(gòu)需求:
    如果系統(tǒng)需要根據(jù)業(yè)務(wù)需求選擇最佳的數(shù)據(jù)結(jié)構(gòu),并且對存儲和操作登錄信息有更多的靈活性,那么使用 Redis 將會是一個不錯的選擇。Redis 支持多種數(shù)據(jù)類型的存儲和操作,包括字符串、哈希表、列表、集合和有序集合等,能夠滿足不同的業(yè)務(wù)需求。
  • 需要持久化支持:
    如果系統(tǒng)需要對登錄信息進行持久化存儲,以防止數(shù)據(jù)丟失,Redis 的持久化功能可以很好地滿足這一需求。

總的來說,使用 Redis 進行登錄適用于需要支持分布式部署、面對高并發(fā)訪問、有靈活的數(shù)據(jù)結(jié)構(gòu)需求以及需要持久化支持的系統(tǒng)場景。通過合理地利用 Redis 的特性,可以更好地滿足上述情況下的需求,提高系統(tǒng)的可擴展性、性能和穩(wěn)定性。

雖然 Spring Boot 應用通常是單體應用,但是在實際運行中,我們也經(jīng)常會遇到多個實例同時運行的情況,這時候就需要使用 Redis 進行分布式 Session 管理。

StringRedisTemplate

StringRedisTemplate是Spring Data Redis提供的一個類,它是一個具體的對象,用于操作Redis數(shù)據(jù)庫中的字符串類型數(shù)據(jù)。

StringRedisTemplate封裝了Redis的操作,并提供了一系列方法來對Redis中的字符串進行讀取、寫入和刪除操作。它是RedisTemplate的一個子類,專門用于處理字符串類型的數(shù)據(jù)。

??使用StringRedisTemplate

首先引入依賴,引入StringRedisTemplate的依賴

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

?常用的方法

StringRedisTemplate提供了多個方法來操作Redis中的字符串類型數(shù)據(jù)。下面是一些常用的方法:

  • opsForValue().set(key, value):將一個字符串類型的值value存儲到Redis中,并指定鍵key。
  • opsForValue().get(key):根據(jù)鍵key獲取對應的字符串類型的值。
  • opsForValue().increment(key, delta):將鍵key所對應的值增加delta,delta可以為負數(shù)。
  • opsForValue().size(key):獲取值的長度。

為什么我們要使用Redis代替Session進行登錄操作

集群session存在共享問題,會導致數(shù)據(jù)丟失

  • 保存相同的數(shù)據(jù),大家互相copy,會有內(nèi)存空間的浪費
  • 我們copy數(shù)據(jù)的時候,是需要有一定的時間的,會有延遲,如果在這個延遲之內(nèi),如果有人來訪問,仍然會造成數(shù)據(jù)不一致的情況

    請?zhí)砑訄D片描述


    如果我們使用Redis的話。
  • Redis是在tomcat外面的存儲,如果任意一臺tomcat都能訪問到Redis,可以實現(xiàn)數(shù)據(jù)共享,儲存在Redis里面的數(shù)據(jù),任何tomcat都可以看到,使用就不存在數(shù)據(jù)丟失的問題
  • Redis讀寫延遲非常低,方便進行內(nèi)存存儲
  • Redis是key-value結(jié)構(gòu)

在這里插入圖片描述

??具體使用

?編寫攔截器

在這里插入圖片描述

RefreshTokenInterceptor.java

在這里插入圖片描述

在攔截器中配置攔截操作

package com.hmdp.utils;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
import com.hmdp.dto.UserDTO;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import static com.hmdp.utils.RedisConstants.LOGIN_USER_KEY;
import static com.hmdp.utils.RedisConstants.LOGIN_USER_TTL;

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_USER_KEY + token;
        Map<Object, Object> userMap = stringRedisTemplate.opsForHash().entries(key);
        // 3.判斷用戶是否存在
        if (userMap.isEmpty()) {
            return true;
        }
        // 5.將查詢到的hash數(shù)據(jù)轉(zhuǎn)為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();
    }
}

在這里插入圖片描述

刷新token的目的

用戶每訪問一次,這個token就會刷新一次,主要用戶一直在操作,這個token就不會消失

但是如果僅僅攔截的是需要登錄的路徑,用戶 訪問 不需要登錄 的路徑 的時候(比如首頁),這個攔截器就不生效,此時token就不會刷新,這樣子,過了token的有效期后,盡管用戶還在訪問,用戶的登錄狀態(tài)卻消失了,這樣肯定不太合理

那么我們就需要在原來的攔截器基礎(chǔ)上再加上一個攔截器

請?zhí)砑訄D片描述

LoginInterceptor.java

在這里插入圖片描述

在攔截器中配置攔截操作

package com.hmdp.utils;

import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class LoginInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 1.判斷是否需要攔截(ThreadLocal中是否有用戶)
        if (UserHolder.getUser() == null) {
            // 沒有,需要攔截,設(shè)置狀態(tài)碼
            response.setStatus(401);
            // 攔截
            return false;
        }
        // 有用戶,則放行
        return true;
    }
}

在這里插入圖片描述

?配置攔截器

我們上面編寫了攔截器,我們還需要配置攔截器,使這個攔截器生效

MvcConfig.java

這里是引用

package com.hmdp.config;

import com.hmdp.utils.LoginInterceptor;
import com.hmdp.utils.RefreshTokenInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import javax.annotation.Resource;

@Configuration
public class MvcConfig implements WebMvcConfigurer {

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // 登錄攔截器
        registry.addInterceptor(new LoginInterceptor())
                .excludePathPatterns(
                        "/shop/**",
                        "/voucher/**",
                        "/shop-type/**",
                        "/upload/**",
                        "/blog/hot",
                        "/user/code",
                        "/user/login"
                ).order(1);
        // token刷新的攔截器
        registry.addInterceptor(new RefreshTokenInterceptor(stringRedisTemplate)).addPathPatterns("/**").order(0);
    }
}

在這里插入圖片描述

??基于Redis實現(xiàn)發(fā)送手機驗證碼操作

??總體思路

在這里插入圖片描述

??具體步驟

我們首先引入上面說的依賴,然后在application.yml文件(或yaml文件)中進行配置,如下

在這里插入圖片描述

下面我們編寫發(fā)送手機驗證碼的核心代碼

@Slf4j
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Override
    public Result sendCode(String phone, HttpSession session) {
        // 1.校驗手機號
        if (RegexUtils.isPhoneInvalid(phone)) {
            // 2.如果不符合,返回錯誤信息
            return Result.fail("手機號格式錯誤!");
        }
        // 3.符合,生成驗證碼
        String code = RandomUtil.randomNumbers(6);

        // 4.保存驗證碼到 redis
        stringRedisTemplate.opsForValue().set(LOGIN_CODE_KEY + phone, code, LOGIN_CODE_TTL, TimeUnit.MINUTES);

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

在這里插入圖片描述

上面代碼里面的RegexUtils.isPhoneInvalid(phone)這段代碼是什么用法

在這里插入圖片描述

stringRedisTemplate.opsForValue().set(LOGIN_CODE_KEY + phone, code, LOGIN_CODE_TTL, TimeUnit.MINUTES);這段代碼有什么用

這段代碼的作用是將一個驗證碼(即code)存儲到Redis中,并設(shè)置了過期時間為LOGIN_CODE_TTL分鐘。以便在一定時間后自動刪除該鍵值對。

??基于Redis實現(xiàn)短信登錄并注冊的操作

??總體思路

在這里插入圖片描述

??具體步驟

我們首先引入上面說的依賴,并且在application.yml文件(或yaml文件)中進行配置(同上)
然后我們來編寫核心代碼

@Slf4j
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Override
    public Result login(LoginFormDTO loginForm, HttpSession session) {
        // 1.校驗手機號
        String phone = loginForm.getPhone();
        if (RegexUtils.isPhoneInvalid(phone)) {
            // 2.如果不符合,返回錯誤信息
            return Result.fail("手機號格式錯誤!");
        }
        // 3.從redis獲取驗證碼并校驗
        String cacheCode = stringRedisTemplate.opsForValue().get(LOGIN_CODE_KEY + phone);
        String code = loginForm.getCode();
        if (cacheCode == null || !cacheCode.equals(code)) {
            // 不一致,報錯
            return Result.fail("驗證碼錯誤");
        }

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

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

        // 7.保存用戶信息到 redis中
        // 7.1.隨機生成token,作為登錄令牌
        String token = UUID.randomUUID().toString(true);
        // 7.2.將User對象轉(zhuǎn)為HashMap存儲
        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()));
        // 7.3.存儲
        String tokenKey = LOGIN_USER_KEY + token;
        stringRedisTemplate.opsForHash().putAll(tokenKey, userMap);
        // 7.4.設(shè)置token有效期
        stringRedisTemplate.expire(tokenKey, LOGIN_USER_TTL, TimeUnit.MINUTES);

        // 8.返回token
        return Result.ok(token);
    }
    
    private User createUserWithPhone(String phone) {
        // 1.創(chuàng)建用戶
        User user = new User();
        user.setPhone(phone);
        user.setNickName(USER_NICK_NAME_PREFIX + RandomUtil.randomString(10));
        // 2.保存用戶
        save(user);
        return user;
    }
    
}

在這里插入圖片描述

User user = query().eq(“phone”, phone).one();這段代碼使用了mybatisplus,相當于select * from tb_user where phone = ?

為什么要使用HashMap進行存儲

在這段代碼中,使用HashMap進行存儲是為了將用戶對象轉(zhuǎn)換成鍵值對形式,便于統(tǒng)一保存到Redis中,并且可以方便地進行序列化和反序列化操作。具體來說:

  • 便于存儲和讀?。簩⒂脩魧ο筠D(zhuǎn)為HashMap后,可以方便地通過stringRedisTemplate.opsForHash().putAll()方法一次性將整個用戶對象存儲到Redis的Hash數(shù)據(jù)結(jié)構(gòu)中,而不需要對用戶對象的每個字段分別進行存儲。
  • 數(shù)據(jù)結(jié)構(gòu)清晰:使用HashMap可以清晰地表示用戶對象的各個字段和對應的數(shù)值,便于管理和維護。
  • 方便序列化和反序列化:HashMap作為Java中的常用數(shù)據(jù)結(jié)構(gòu),可以方便地進行序列化(將數(shù)據(jù)轉(zhuǎn)換為字節(jié)序列)反序列化(將字節(jié)序列轉(zhuǎn)換為數(shù)據(jù))操作,便于在存儲到Redis或者從Redis中讀取時進行數(shù)據(jù)格式的轉(zhuǎn)換。

總之,使用HashMap進行存儲能夠簡化代碼邏輯,提高數(shù)據(jù)存儲和讀取的效率,并且方便進行數(shù)據(jù)結(jié)構(gòu)的轉(zhuǎn)換和管理。

Map<String, Object> userMap = BeanUtil.beanToMap(userDTO, new HashMap<>(),
CopyOptions.create()
.setIgnoreNullValue(true)
.setFieldValueEditor((fieldName, fieldValue) -> fieldValue.toString()));
這段代碼為什么要這樣寫,這些參數(shù)有什么用

其中,beanToMap是一個方法,用于將Java對象(Bean)轉(zhuǎn)換為Map類型的數(shù)據(jù)結(jié)構(gòu)。

在這段代碼中,BeanUtil.beanToMap()方法被使用,它是一個工具類方法,可以通過反射機制將Java對象的屬性和對應的值轉(zhuǎn)換為鍵值對形式,并存儲到一個Map對象中。

具體來說,beanToMap方法接收三個參數(shù):

  • userDTO:表示要轉(zhuǎn)換的源對象,即需要將其轉(zhuǎn)換為Map的對象。

  • new HashMap<>():表示用于存儲轉(zhuǎn)換結(jié)果的目標HashMap對象,這里使用了一個新的空HashMap,用于接收轉(zhuǎn)換后的鍵值對數(shù)據(jù)。

  • CopyOptions.create().setIgnoreNullValue(true):這是使用BeanUtil進行對象轉(zhuǎn)換時的配置選項。setIgnoreNullValue(true)表示忽略源對象中值為null的屬性,不將其放入目標Map中。

  • .setFieldValueEditor((fieldName, fieldValue) -> fieldValue.toString()):這個配置項表示對轉(zhuǎn)換過程中的字段值進行編輯處理。在這里,它的作用是將字段值轉(zhuǎn)換為字符串類型,確保最終存儲到Map中的值都是字符串類型。

綜合起來,這段代碼的目的是將UserDTO對象轉(zhuǎn)換為Map類型,同時忽略空值屬性,并確保所有屬性值都被轉(zhuǎn)換為字符串類型。這樣做的原因可能是為了在存儲到Redis中時,確保數(shù)據(jù)的統(tǒng)一性和一致性,便于后續(xù)從Redis中讀取并進行處理。

在這里插入圖片描述

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

相關(guān)文章

  • java同步器AQS架構(gòu)AbstractQueuedSynchronizer原理解析下

    java同步器AQS架構(gòu)AbstractQueuedSynchronizer原理解析下

    這篇文章主要為大家介紹了java同步器AQS架構(gòu)AbstractQueuedSynchronizer原理解析下,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步
    2022-03-03
  • JavaWeb開發(fā)入門第一篇必備知識講解

    JavaWeb開發(fā)入門第一篇必備知識講解

    JavaWeb開發(fā)入門第一篇主要內(nèi)容介紹的是必備知識、基礎(chǔ)知識、搭建JavaWeb應用開發(fā)環(huán)境,感興趣的小伙伴們可以參考一下
    2016-04-04
  • Java中雙大括號初始化的理解與使用

    Java中雙大括號初始化的理解與使用

    最近重讀Java 編程思想,讀到有關(guān)實例化代碼塊兒的內(nèi)容,使我對于使用兩個大括號進行初始化有了更深的理解,下面這篇文章主要給大家介紹了關(guān)于Java中雙大括號初始化的理解與使用的相關(guān)資料,需要的朋友可以參考下
    2022-06-06
  • 一文帶你入門SpringMVC的配置與使用

    一文帶你入門SpringMVC的配置與使用

    Spring MVC是Spring Framework的一部分,是基于Java實現(xiàn)MVC的輕量級Web框架。本文將通過一些簡單示例帶大家掌握SpringMVC的配置與使用,感興趣的可以了解一下
    2022-11-11
  • Java 使用openoffice進行word轉(zhuǎn)換為pdf的方法步驟

    Java 使用openoffice進行word轉(zhuǎn)換為pdf的方法步驟

    這篇文章主要介紹了Java 使用openoffice進行word轉(zhuǎn)換為pdf的方法步驟,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2021-04-04
  • Java反射(JDK)與動態(tài)代理(CGLIB)詳解

    Java反射(JDK)與動態(tài)代理(CGLIB)詳解

    下面小編就為大家?guī)硪黄獪\談Java反射與動態(tài)代理。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2021-08-08
  • 使用java采集京東商城區(qū)劃數(shù)據(jù)示例

    使用java采集京東商城區(qū)劃數(shù)據(jù)示例

    這篇文章主要介紹了java采集京東的全國區(qū)劃數(shù)據(jù)示例,保存成json形式,如想轉(zhuǎn)換到數(shù)據(jù)庫只需反序列化為對象保存到數(shù)據(jù)庫即可
    2014-03-03
  • Spring Boot實現(xiàn)通用的接口參數(shù)校驗

    Spring Boot實現(xiàn)通用的接口參數(shù)校驗

    本文介紹基于 Spring Boot 和 JDK8 編寫一個 AOP ,結(jié)合自定義注解實現(xiàn)通用的接口參數(shù)校驗。具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2018-05-05
  • 詳解MyBatis-Plus updateById方法更新不了空字符串/null解決方法

    詳解MyBatis-Plus updateById方法更新不了空字符串/null解決方法

    這篇文章主要介紹了詳解MyBatis-Plus updateById方法更新不了空字符串/null解決方法,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2020-09-09
  • Java中常見字符串拼接九種方式詳細例子

    Java中常見字符串拼接九種方式詳細例子

    這篇文章主要給大家介紹了關(guān)于Java中常見字符串拼接的九種方式,字符串拼接是我們在Java代碼中比較經(jīng)常要做的事情,就是把多個字符串拼接到一起,文中通過代碼介紹的非常詳細,需要的朋友可以參考下
    2023-07-07

最新評論