Redis基礎應用Demo

一、Redis常用命令操作

1、String類型

賦值命令:set key value

取值命令:get key

數字遞增:incr key

數字遞減:incr key

增加指定的整數:incrby key increment

減少指定的整數:decrby key decrement

E:\Redis>redis-cli.exe
127.0.0.1:6379> set test:name zhangsan
OK
127.0.0.1:6379> get test:name
"zhangsan"
127.0.0.1:6379> incr num  -- 自動初始化num,並返回加1後的結果
(integer) 1
127.0.0.1:6379> decr num  -- 返回減1後的結果
(integer) 0
127.0.0.1:6379> incrby num 5 -- 返回加5後的結果
(integer) 5
127.0.0.1:6379> decrby num 4 -- 返回減5後的結果
(integer) 1
127.0.0.1:6379>

2、Hash類型

賦值命令:hset key field value 返回1表示新的 Field 被設置了新值,0表示Field已經存在,用新值覆蓋原有值

取值指令:hget key field

刪除指令:hdel key field

127.0.0.1:6379> hset test:teacher username zhangsan -- value是hash類型包括key-value
(integer) 1
127.0.0.1:6379> hget test:teacher username
"zhangsan"
127.0.0.1:6379> hset test:person id 1
(integer) 1
127.0.0.1:6379> hget test:person id
"1"
127.0.0.1:6379> hset test:person id 2
(integer) 0
127.0.0.1:6379> hdel test:person id
(integer) 1

3、列表類型list

list可以做成棧和隊列,如果是左進右出就是棧,左進左出就是隊列

向列表頭部添加元素:lpush key value [value … ] 插入成功後返回列表中的元素個數

向列表尾部添加元素:rpushkey value [value …] 插入成功後返回列表中的元素個數

元素從左邊出棧 : lpop key 第一步是將列表左邊的元素從列表中移除,第二步是返回被移除的元素值

元素從右邊出棧 : rpop key 第一步是將列表右邊的元素從列表中移除,第二步是返回被移除的元素值

獲取列表中元素的個數:llen key

獲取指定索引的元素值:lindex key index 該命令將返回鏈表中指定位置(index)的元素,index 是0-based

獲得列表:lrange key start stop 返回指定範圍內元素的列表。

127.0.0.1:6379> lpush list 1 2 3 4 5
(integer) 5
127.0.0.1:6379> lpop list
"5"
127.0.0.1:6379> llen list
(integer) 4
127.0.0.1:6379> lindex list 2
"2"
127.0.0.1:6379> rpop list
"1"

4、無序集合set

增加元素:sadd key memer 返回實際插入到集合中元素的個數

獲得集合中元素個數:scard key

從集合中彈出一個元素:spop key 返回移除的成員 (任意的)

判斷元素是否在集合中:sismemer key member 返回1表示已經存在,0表示不存在

127.0.0.1:6379> sadd myset 1
(integer) 1
127.0.0.1:6379> sadd myset 2 3 4
(integer) 3
127.0.0.1:6379> scard myset
(integer) 4
127.0.0.1:6379> spop myset
"2"
127.0.0.1:6379> sismember myset 3
(integer) 1
127.0.0.1:6379>

5、有序集合sorted sort

增加元素:zadd key score member [score] [member ] 添加成功返回實際插入的成員數量

獲得集合中元素個數:zcard key

獲得指定分數範圍內的元素個數:zcount key min max 該命令用於獲取分數(score)在min和max之間的成員數量

獲得元素的分數:zscore key member

增加某個元素的分數:zincrby key increment member

獲得元素的排名:zrank key member

127.0.0.1:6379> zadd myzset 1 a 2 b 3 c
(integer) 3
127.0.0.1:6379> zcard myzset
(integer) 3
127.0.0.1:6379> zcount myzset 1 3
(integer) 3
127.0.0.1:6379> zscore myzset a
"1"
127.0.0.1:6379> zscore myzset c
"3"
127.0.0.1:6379> zincrby myzset 2 a
"3"
127.0.0.1:6379> zscore myzset a
"3"
127.0.0.1:6379> zrank myzset a
(integer) 1
127.0.0.1:6379> zrank myzset c
(integer) 2
127.0.0.1:6379>

二、分佈式共享session

實現思路:
(1) 登錄頁面提交用戶名密碼。
(2) 登錄成功後生成token。Token相當於原來的sessionid,字符串,可以使用uuid。
(3) 把用戶信息保存到redis。Key就是token,value就是userId。
(4) 設置key的過期時間。模擬Session的過期時間。一般一個小時。
(5) 攔截器攔截請求校驗 sessionId

1、LoginReqVO 登錄用戶用來提交用戶名和密碼

@Data
public class LoginReqVO {
    @ApiModelProperty(value = "用戶名")
    private String username;

    @ApiModelProperty(value = "密碼")
    private String password;
}

2、LoginRespVO 將用戶信息和token響應給瀏覽器

@Data
public class LoginRespVO {
    @ApiModelProperty(value = "用戶認證憑證")
    private String token;

    @ApiModelProperty(value = "用戶id")
    private String userId;
}

3、UserServiceImpl

@Service
public class UserServiceImpl implements UserService {
    @Autowired
    private SysUserMapper sysUserMapper;

    @Autowired
    private RedisTemplate redisTemplate;
    @Override
    public LoginRespVO login(LoginReqVO vo) {
        //根據提交的用戶名到數據庫彙總查找該用戶
        SysUser sysUser = sysUserMapper.selectByUsername(vo.getUsername());

        //如果用戶不存在
        if(sysUser==null){
            throw new BusinessException(4001005,"不存在該用戶,請先註冊");
        }

        //如果用戶狀態爲禁用
        if(sysUser.getStatus()==2){
            throw new BusinessException(4001006,"該賬號已被禁用請聯繫系統管理員");
        }

        //如果用戶密碼不正確(將明文加密後再與數據庫中的密碼進行比較)
 if(!PasswordUtils.matches(sysUser.getSalt(),vo.getPassword(),sysUser.getPassword())){
            throw new BusinessException(4001007,"用戶名密碼不匹配");
        }

        //登錄成功,生成token
        String token= UUID.randomUUID().toString();

        //將token和用戶信息響應給瀏覽器
        LoginRespVO respVO=new LoginRespVO();
        respVO.setUserId(sysUser.getId());
        respVO.setToken(token);

        //將用戶信息在redis緩存中,key是token,值是用戶信息,同時設置token過期時間爲60s
        redisTemplate.opsForValue().set(token,sysUser.getId(),60, TimeUnit.MINUTES);
        return respVO;
    }
}

4、UserController

@RestController
@RequestMapping("/api")
@Api(tags = "用戶模塊")
public class UserController {

    @Autowired
    private UserService userService;

    @PostMapping("/user/login")
    @ApiOperation(value = "用戶登錄接口")
    public LoginRespVO login(@RequestBody LoginReqVO vo){
        return userService.login(vo);
    }
}

5、配置攔截路徑:登錄註冊請求不能攔截,需要放行

@Configuration
public class WebAppConfig implements WebMvcConfigurer {
    @Bean
    public TokenInterceptor tokenInterceptor(){
        return new TokenInterceptor();
    }

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(tokenInterceptor())
.addPathPatterns("/api/**").excludePathPatterns("/api/user/login","/api/user/register","/api/user/code/*");
    }
}

6、TokenInterceptor配置攔截器:對於除登錄註冊外的其他請求,都要進行攔截,判斷用戶是否處於登錄狀態。

public class TokenInterceptor implements HandlerInterceptor {
    @Autowired
    private RedisService redisService;

    private RedisTemplate redisTemplate;

    //在執行Controller層方法之前攔截登錄請求,驗證用戶是否處於登錄狀態
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

        //從請求頭中獲取token
        String token=request.getHeader("token");
        //判斷token是否爲空
        if(StringUtils.isEmpty(token)){
            throw new BusinessException(4001002,"用戶憑證不能爲空,請重新登錄");
        }else {
            //如果token不爲空,需要判斷token是否有效
            if(!redisTemplate.hasKey(token)){
                throw new BusinessException(4001002,"用戶憑證無效,請重新登錄");
            }
        }
        return true;
    }
}

三、異地登錄提醒下線

最近接到產品提的一個需求說,一個賬號同時只能在一個地方登錄,如果在其他地方登錄則提示已在別處登錄,同時,同一瀏覽器同時只能登錄一個用戶。

實現思路:

(1) 登錄頁面提交用戶名密碼。

(2) 登錄成功後生成token。

(3) 把用戶信息保存到redis。Key就是token,value就是userId。

(4) 設置key的過期時間。

(5) 把token存入redis,key爲 userId,value 就是 token

(6) 攔截器攔截請求校驗 token。

(7) 獲取 userId 後再去比較 header 攜帶的token和redis標記的token是否一致,不一致則提示用戶已經異地登錄。

1、 LoginReqVO

@Data
public class LoginReqVO {
    @ApiModelProperty(value = "用戶名")
    private String username;

    @ApiModelProperty(value = "密碼")
    private String password;
}

2、LoginRespVO

@Data
public class LoginRespVO {
    @ApiModelProperty(value = "用戶認證憑證")
    private String token;

    @ApiModelProperty(value = "用戶id")
    private String userId;
}

3、 UserServiceImpl :用戶登錄成功後,生成token並響應給瀏覽器

@Service
public class UserServiceImpl implements UserService {
    @Autowired
    private SysUserMapper sysUserMapper;

    @Autowired
    private RedisTemplate redisTemplate;
    
    @Override
    public LoginRespVO login(LoginReqVO vo) {
        //根據提交的用戶名到數據庫彙總查找該用戶
        SysUser sysUser = sysUserMapper.selectByUsername(vo.getUsername());

        //如果用戶不存在
        if(sysUser==null){
            throw new BusinessException(4001005,"不存在該用戶,請先註冊");
        }

        //如果用戶狀態爲禁用
        if(sysUser.getStatus()==2){
            throw new BusinessException(4001006,"該賬號已被禁用請聯繫系統管理員");
        }

        //如果用戶密碼不正確
        if(!PasswordUtils.matches(sysUser.getSalt(),vo.getPassword(),sysUser.getPassword())){
            throw new BusinessException(4001007,"用戶名密碼不匹配");
        }

        //登錄成功,生成token
        String token= UUID.randomUUID().toString();

        //將token和用戶信息響應給瀏覽器
        LoginRespVO respVO=new LoginRespVO();
        respVO.setUserId(sysUser.getId());
        respVO.setToken(token);

        //將用戶信息在redis緩存中,key是token,值是用戶信息,過期時間爲60分鐘
        redisTemplate.opsForValue().set(token,sysUser.getId(),60, TimeUnit.MINUTES);

        //標記token,把token存入redis緩存中,key是userId,值爲token,過期時間爲60分鐘
        redisTemplate.opsForValue().set(sysUser.getId(),token,60,TimeUnit.MINUTES);
        return respVO;
    }
}

4、UserController:

@RestController
@RequestMapping("/api")
@Api(tags = "用戶模塊")
public class UserController {

    @Autowired
    private UserService userService;

    @PostMapping("/user/login")
    @ApiOperation(value = "用戶登錄接口")
    public LoginRespVO login(@RequestBody LoginReqVO vo){
        return userService.login(vo);
    }
}

5、配置攔截器攔截路徑:放行登錄

@Configuration
public class WebAppConfig implements WebMvcConfigurer {
    @Bean
    public TokenInterceptor tokenInterceptor(){
        return new TokenInterceptor();
    }

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(tokenInterceptor())
                .addPathPatterns("/api/**").excludePathPatterns("/api/user/login","/api/user/register","/api/user/code/*");
    }
}

6、配置攔截器:判斷前端穿過類的token和redis緩存中標記的token是否一致

public class TokenInterceptor implements HandlerInterceptor {
    @Autowired
    private RedisService redisService;

    private RedisTemplate redisTemplate;

    //在執行Controller層方法之前攔截登錄請求,驗證用戶是否處於登錄狀態
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

        //從請求頭中獲取token
        String token=request.getHeader("token");
        //判斷token是否爲空
        if(StringUtils.isEmpty(token)){
            throw new BusinessException(4001002,"用戶憑證不能爲空,請重新登錄");
        }else {
            //判斷緩存中token是否過期
            if(!redisTemplate.hasKey(token)){
                throw new BusinessException(4001002,"用戶憑證無效,請重新登錄");
            }

            //根據鍵token獲取值userId
            String userId= (String) redisTemplate.opsForValue().get(token);

            //如果redis中不存在userId或者瀏覽器傳過來的token和redis緩存中的token不匹配
if(redisTemplate.hasKey(userId)&&!token.equals(redisTemplate.opsForValue().get(userId)){
                throw new BusinessException(4001002,"您的賬號已經在異地登錄,請重新登錄");
            }
        }
        return true;
    }
}

四、註冊短信驗證碼

短信驗證碼是所有項目必不可少的基礎功能模塊之一,假如突然有一天你領導給你佈置的一個需求。在用戶註冊的時候要校驗手機號。

要求如下:
(1) 註冊的時候校驗手機號
(2) 每個手機號每天最多發送五條註冊短信驗證碼
(3) 驗證碼5分鐘內有效。

思路:
(1) 發送前驗證手機號是否符合要求。
(2) 生成短信驗證碼。
(3) 發送驗證碼到手機。
(4) 把驗證碼存入redis
(5) 標記手機號
(6) 註冊的時候校驗手機號和驗證碼是否正確

1、RegisterReqVO

@Data
public class RegisterReqVO {
    @ApiModelProperty(value = "賬號")
    private String username;
    @ApiModelProperty(value = "手機號")
    private String phone;
    @ApiModelProperty(value = "密碼")
    private String password;
    @ApiModelProperty(value = "驗證碼")
    private String code;
}

2、 Contant:構建redis的key

 */
public class Contant {

    /**
     * 判斷是否達上線的key
     */
    public final static String REGISTER_CODE_COUNT_KEY="register-code-count-key_";

    /**
     * 驗證碼有效期key
     */
    public final static String REGISTER_CODE_COUNT_VALIDITY_KEY="register-code-count-validity-key_";
}

3、UserServiceImpl

@Service
public class UserServiceImpl implements UserService {
    @Autowired
    private SysUserMapper sysUserMapper;

    @Autowired
    private RedisTemplate redisTemplate;
    
    /**
     * 獲取驗證碼
     */
    @Override
    public String getCode(String phone) {
        //驗證手機號是否合法
        Pattern pattern = Pattern.compile("^1(3|4|5|7|8)\\d{9}$");
        Matcher matcher = pattern.matcher(phone);
        if(!matcher.matches()) {
            throw  new BusinessException(4001004,"手機號格式錯誤");
        }
        //每次獲取驗證碼就讓次數加1,判斷獲取手機號驗證碼是否超限
        long count = redisTemplate.opsForValue().increment(Contant.REGISTER_CODE_COUNT_KEY+phone,1);
        if(count>5){
            throw new BusinessException(4001004,"當日發送已達上限");
        }

        //生成6位隨機數,作爲驗證碼
        String code=generateCode();
      
        //將生成的驗證碼存入 redis 過期時間爲 5 分鐘
        redisService.set(Contant.REGISTER_CODE_COUNT_VALIDITY_KEY+phone,code,5,TimeUnit.MINUTES;
        //發送短信這裏用輸出模擬
        System.out.println(code);
        return code;
    }

    /**
     * 生成六位驗證碼
     */
    private String generateCode(){
        Random random = new Random();
        int x = random.nextInt(899999);
        String code = String.valueOf(x + 100000);
        return code;
    }

    /**
     * 用戶註冊
     */
    @Override
    public String register(RegisterReqVO vo) {

        //判斷驗證碼是否有效
        if(!redisTemplate.hasKey(Contant.REGISTER_CODE_COUNT_VALIDITY_KEY+vo.getPhone()){
            throw new BusinessException(4001008,"驗證碼已失效請重新獲取");
        }
        //校驗驗證碼是否正確(前端傳過來的驗證碼和redis緩存中的是否一致)
        if(!vo.getCode().equals(redisTemplate.opsForValue().get(Contant.REGISTER_CODE_COUNT_VALIDITY_KEY+vo.getPhone()))){
         throw new BusinessException(4001009,"請輸入正確的驗證碼");
        }

        SysUser sysUser = sysUserMapper.selectByUsername(vo.getUsername());
        if(sysUser!=null){
            throw new BusinessException(4001010,"該用戶名已被註冊");
        }

        //構造user用戶信息存入數據庫中
        SysUser user=new SysUser();
        //屬性複製
        BeanUtils.copyProperties(vo,user);
        user.setId(UUID.randomUUID().toString());
        user.setCreateTime(new Date());
        String salt=PasswordUtils.getSalt();
        String ecdPwd=PasswordUtils.encode(vo.getPassword(),salt);
        user.setSalt(salt);
        user.setPassword(ecdPwd);
        int i = sysUserMapper.insertSelective(user);

        if(i!=1){
            throw new BusinessException(4001011,"操作失敗");
        }

        //註冊成功時將redis緩存清除
        redisTemplate.delete(Contant.REGISTER_CODE_COUNT_VALIDITY_KEY+vo.getPhone());
        redisTemplate.delete(Contant.REGISTER_CODE_COUNT_KEY+vo.getPhone());

        return "註冊成功";
    }
}

4、UserController

@RestController
@RequestMapping("/api")
@Api(tags = "用戶模塊")
public class UserController {

    @Autowired
    private UserService userService;

    @PostMapping("/user/register")
    @ApiOperation(value = "用戶註冊接口")
    public String register(@RequestBody RegisterReqVO vo){
        return userService.register(vo);
    }
    
    @GetMapping("/user/code/{phone}")
    public String getCode(@PathVariable("phone") String phone){
        return userService.getCode(phone);
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章