Redis的安装和启动
Redis是用C语言开发的一个高性能键值对数据库,可用于数据缓存,主要用于处理大量数据的高访问负载。
- 下载Redis,下载地址:https://github.com/MicrosoftArchive/redis/releases
- 下载完后解压到指定目录
- 在当前地址栏输入cmd后,执行redis的启动命令:redis-server.exe redis.windows.conf
添加项目依赖
在pom.xml中新增Redis相关依赖
<!--redis依赖配置-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
修改SpringBoot配置文件
在application.yml中添加Redis的配置及Redis中自定义key的配置。
#在spring节点下添加Redis的配置
redis:
host: localhost # Redis服务器地址
database: 0 # Redis数据库索引(默认为0)
port: 6379 # Redis服务器连接端口
password: # Redis服务器连接密码(默认为空)
jedis:
pool:
max-active: 8 # 连接池最大连接数(使用负值表示没有限制)
max-wait: -1ms # 连接池最大阻塞等待时间(使用负值表示没有限制)
max-idle: 8 # 连接池中的最大空闲连接
min-idle: 0 # 连接池中的最小空闲连接
timeout: 3000ms # 连接超时时间(毫秒)
在根节点下添加Redis自定义key的配置
# 自定义redis key
redis:
key:
prefix:
authCode: "portal:authCode:"
expire:
authCode: 120 # 验证码超期时间
添加RedisUtils工具类
package org.yxin.common.utils;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.temporal.ChronoUnit;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
/**
* redis基本操作工具类
*/
@Slf4j
@Component
@AllArgsConstructor
public class RedisUtils {
private final RedisTemplate redisTemplate;
/**
* 存储普通对象
* @param key
* @param value
*/
public void set(final String key,final Object value){
redisTemplate.opsForValue().set(key,value);
}
/**
* 存储普通对象带有效期,时间单位为秒
* @param key
* @param value
*/
public void set(final String key,final Object value,long timeout){
redisTemplate.opsForValue().set(key,value,timeout, TimeUnit.SECONDS);
}
/**
* 存储普通对象带有效期,时间单位自设定
* @param key
* @param value
*/
public void set(final String key,final Object value,final long timeout,TimeUnit timeUnit){
redisTemplate.opsForValue().set(key,value,timeout,timeUnit);
}
/**
* 存储普通对象,时间为当天24时
* @param key
* @param value
*/
public void setToday(final String key,final Object value){
LocalDateTime midnight = LocalDateTime.ofInstant(new Date().toInstant(), ZoneId.systemDefault())
.plusDays(1).withHour(0).withMinute(0);
LocalDateTime currentDateTime = LocalDateTime.ofInstant(new Date().toInstant(), ZoneId.systemDefault());
long seconds = ChronoUnit.SECONDS.between(currentDateTime,midnight);
redisTemplate.opsForValue().set(key,value,seconds,TimeUnit.SECONDS);
}
/**
* 获取普通对象
*/
public Object get(String key){
return redisTemplate.opsForValue().get(key);
}
/**
* 判断key是否存在
*/
public Boolean hasKey(final String key){
return redisTemplate.hasKey(key);
}
/**
* 设置有效期
*/
public boolean expire(final String key, final long timeout){
return expire(key,timeout,TimeUnit.SECONDS);
}
/**
* 设置有效期 true 成功,false失败
*/
public boolean expire(final String key, final long timeout,final TimeUnit unit){
Boolean ret = redisTemplate.expire(key,timeout,unit);
return ret != null && ret;
}
/**
* 删除单个key
*/
public boolean del(final String key){
Boolean ret = redisTemplate.delete(key);
return ret !=null && ret;
}
/**
* 删除多个key
*/
public long del(final Collection<String> keys){
Long ret = redisTemplate.delete(keys);
return ret == null? 0 :ret;
}
/**
* 存储hash操作
*/
public void hPut(final String key,final String hKey,final Object value){
redisTemplate.opsForHash().put(key,hKey,value);
}
/**
* 获取hash中的数据
*/
public Object hGet(final String key,final String hKey){
return redisTemplate.opsForHash().get(key,hKey);
}
/**
* 往list中存入数据
*/
public long lPush(final String key,final Object value){
Long count = redisTemplate.opsForList().rightPush(key,value);
return count == null ? 0:count;
}
/**
* 往list中存入list数据
*/
public long lPushAll(final String key,final Collection<Object> values){
Long count = redisTemplate.opsForList().rightPushAll(key,values);
return count == null ? 0:count;
}
/**
* 往list中存入多个数据
*/
public long lPushAll(final String key,final Object... values){
Long count = redisTemplate.opsForList().rightPushAll(key,values);
return count == null ? 0:count;
}
/**
* 从list中获取begin到end之间的数据
*/
public List<Object> lGet(final String key, final int start, final int end){
return redisTemplate.opsForList().range(key,start,end);
}
/**
* 加锁
*/
public boolean lock(String key , long expire){
String lock = key;
return (Boolean)redisTemplate.execute((RedisCallback) connection ->{
long expireAt = System.currentTimeMillis()+expire+1;
Boolean acquire = connection.setNX(lock.getBytes(),String.valueOf(expireAt).getBytes());
if(acquire){
return true;
} else {
byte[] value = connection.get(lock.getBytes());
if(Objects.nonNull(value)&&value.length>0){
long expireTime = Long.parseLong(new String(value));
//如果锁已经过期
if(expireTime<System.currentTimeMillis()){
//重新加锁
byte[] oldValue = connection.getSet(lock.getBytes(),String.valueOf(
System.currentTimeMillis() +expire+1).getBytes());
return Long.parseLong(new String(oldValue))<System.currentTimeMillis();
}
}
}
return false;
});
}
}
添加UmsMemberController
package org.yxin.controller;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;
import org.yxin.common.api.CommonResult;
import org.yxin.service.UmsMemberService;
/**
* 根据电话号码获取验证码的接口和校验验证码的接口
* RequiredArgsConstructor 会将类的每一个final字段或者non-null字段生成一个构造方法
* @AllArgsConstructor 生成一个包含所有字段的构造方法,如果使用了@Value注入,@Value注入会失效,原因时因为@Value注解是通过对象的set方法赋值的,构造方法的执行还在set方法之前,所以在构造方法中使用变量会变量为null
*/
@RestController
@RequiredArgsConstructor
@Api(tags = "UmsMemberController",description = "会员登录注册管理")
@RequestMapping("/sso")
public class UmsMemberController {
private final UmsMemberService umsMemberService;
/**
* 获取验证码
*/
@ApiOperation("获取验证码")
@RequestMapping(value ="/getAuthCode",method = RequestMethod.GET)
@ResponseBody
public CommonResult getAuthCode(@RequestParam String telephone){
return umsMemberService.generateAuthCode(telephone);
}
@ApiOperation("判断验证码是否正确")
@RequestMapping(value = "/verifyAuthCode", method = RequestMethod.POST)
@ResponseBody
public CommonResult updatePassword(@RequestParam String telephone,
@RequestParam String authCode) {
return memberService.verifyAuthCode(telephone,authCode);
}
}
添加UmsMemberService接口
package org.yxin.service;
import org.yxin.common.api.CommonResult;
/**
* 会员管理service
*/
public interface UmsMemberService {
/**
* 生产验证码
* @return
*/
CommonResult generateAuthCode(String telephone);
/**
* 判断验证码和手机号是否匹配
*/
CommonResult verifyAuthCode(String telephone,String authCode );
}
添加UmsMemberService接口的实现类UmsMemberServiceImpl
生成验证码时,将自定义的Redis键值加上手机号生成一个Redis的key,以验证码为value存入到Redis中,并设置过期时间为自己配置的时间(这里为120s)。校验验证码时根据手机号码来获取Redis里面存储的验证码,并与传入的验证码进行比对。
package org.yxin.service.impl;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.yxin.common.api.CommonResult;
import org.yxin.common.utils.RedisUtils;
import org.yxin.service.UmsMemberService;
import java.util.Random;
/**
* 会员管理实现类
*/
@Service
@RequiredArgsConstructor
@Slf4j
public class UmsMemberServiceImpl implements UmsMemberService {
private final RedisUtils redisUtils;
@Value("${redis.key.prefix.authCode}")
private String REDIS_KEY_PREFIX_AUTH_CODE;
@Value("${redis.key.expire.authCode}")
private Long AUTH_CODE_EXPIRE_SECONDS;
@Override
public CommonResult generateAuthCode(String telePhone){
StringBuffer sb = new StringBuffer();
Random random = new Random();
for(int i=0;i<6;i++){
sb.append(random.nextInt(10));
}
redisUtils.set(REDIS_KEY_PREFIX_AUTH_CODE+telePhone,sb.toString());
redisUtils.expire(REDIS_KEY_PREFIX_AUTH_CODE+telePhone,AUTH_CODE_EXPIRE_SECONDS);
return CommonResult.success(sb.toString(),"获取验证码成功");
}
@Override
public CommonResult verifyAuthCode(String telePhone,String authCode){
if(StringUtils.isEmpty(authCode)){
log.error("验证码为空");
return CommonResult.failed("验证码为空!");
}
String code = (String) redisUtils.get(REDIS_KEY_PREFIX_AUTH_CODE+telePhone);
if(authCode.equals(code)){
return CommonResult.success(null,"验证码效验成功!");
} else {
return CommonResult.failed("验证码效验失败!");
}
}
}