Spring Boot过滤非法请求
背景
接口过滤我对他的定义就是:当满足特定的条件下才给用户访问接口的权限。
不过这里我指定并不是权限校验,那是JWT做的事,这里我们主要研究的是对分发访问的过滤。
在最近的项目中整合了短信验证码的发送,对于短信验证码、OSS等云服务来说,非法请求可能会为你带来巨额的账单,所以进行端口的过滤是十分有必要的。
业务场景
这里我们假定有一个短信发送的服务/user/code
我们考虑以下几种场景的拦截
用户获取验证码后,60S内无法获取新的验证码
这个校验,通常是由前端页面来实现的。但是,如果没有在后端进行相应的判断,刷新页面后,仍然是可以重新获取验证码的。
所以,我们有必要在后端也进行判断。
package cn.rayfoo.modules.base.interceptor;
import cn.rayfoo.common.exception.MyException;
import cn.rayfoo.common.response.HttpStatus;
import cn.rayfoo.common.util.redis.RedisUtil;
import cn.rayfoo.common.util.validate.FormValidatorUtil;
import cn.rayfoo.modules.base.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.lang.Nullable;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @author rayfoo@qq.com
* @version 1.0
* @date 2020/8/6 9:03
* @description 短信校验拦截器,由于在配置类new方式创建 所以无需在此处交由Spring管理
*/
public class SMSValidateInterceptor implements HandlerInterceptor {
/**
* RedisUtil工具类
*/
@Autowired
private RedisUtil redisUtil;
/**
* 注入userService
*/
@Autowired
private UserService userService;
/**
* 请求处理之前调用 一般用作权限校验
*
* @return 是否放行
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//获取用户的手机号码
String phoneNumber = request.getParameter("phoneNumber");
//手机号非空校验
if(StringUtils.isEmpty(phoneNumber)){
throw MyException.builder().code(HttpStatus.INTERNAL_SERVER_ERROR.value()).msg("请输入手机号哦!").build();
}
//校验手机号正则
boolean mobile = FormValidatorUtil.isMobile(phoneNumber);
if (!mobile) {
//交由全局异常处理
throw new MyException(HttpStatus.INTERNAL_SERVER_ERROR.value(), "请输入正确的手机号码");
}
//判断这个手机号是否已经注册了
boolean exists = userService.phoneIsNotExists(phoneNumber);
if (!exists) {
throw MyException.builder().code(HttpStatus.INTERNAL_SERVER_ERROR.value()).msg("该号码已存在,请更换手机号码").build();
}
//判断手机号是否在redis中存在(一分钟内已经获取过验证码)
if (redisUtil.hasKey(phoneNumber)) {
//获取redisKey的过期时间
long seconds = redisUtil.getExpire(phoneNumber);
//如果还没过期
if (seconds > 0) {
//如果股哦其时间未到 抛出异常
throw MyException.builder().code(HttpStatus.INTERNAL_SERVER_ERROR.value()).msg("您的操作过于频繁,请" + seconds + "秒后重试!").build();
}
}
//满足上述条件就放行
return true;
}
/**
* 请求处理之后 视图渲染之前调用 controller方法执行之后 可以进行一些渲染
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception {
}
/**
* 在整个请求结束之后被调用,也就是在DispatcherServlet 渲染了对应的视图之后执行(主要是用于进行资源清理工作)
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {
}
}
用户使用不同的手机号码,请求接口
对于上述的代码可以做到简单的校验;但是如果用户获取验证码失败后,刷新页面使用新的手机号继续获取。并且一直重复上述的操作,也会造成费用的急速增加。
我们可以借助获取用户访问ip,来判断其在一段时间内的访问次数,以此来作为另一个判读依据。这种方法不止适用于手机号码获取的业务,可以封装为一个公共的拦截器来使用。
客户端访问信息工具类
在介绍业务之前,我们先来认识以下用于获取客户访问信息的工具类:
package cn.rayfoo.common.util.net;
import javax.servlet.http.HttpServletRequest;
import java.net.InetAddress;
import java.net.UnknownHostException;
/**
* @author rayfoo@qq.com
* @version 1.0
* @date 2020/8/5 22:51
* @description 获取客户端信息的工具类
*/
public class ClientUtil {
private static final String UNKNOWN = "unknown";
private static final String LOCALHOST = "127.0.0.1";
private static final String SEPARATOR = ",";
/**
* 获取客户端ip地址
* @param request
* @return
*/
public static String getIpAddr(HttpServletRequest request) {
System.out.println(request);
String ipAddress;
try {
ipAddress = request.getHeader("x-forwarded-for");
if (ipAddress == null || ipAddress.length() == 0 || UNKNOWN.equalsIgnoreCase(ipAddress)) {
ipAddress = request.getHeader("Proxy-Client-IP");
}
if (ipAddress == null || ipAddress.length() == 0 || UNKNOWN.equalsIgnoreCase(ipAddress)) {
ipAddress = request.getHeader("WL-Proxy-Client-IP");
}
if (ipAddress == null || ipAddress.length() == 0 || UNKNOWN.equalsIgnoreCase(ipAddress)) {
ipAddress = request.getRemoteAddr();
if (LOCALHOST.equals(ipAddress)) {
InetAddress inet = null;
try {
inet = InetAddress.getLocalHost();
} catch (UnknownHostException e) {
e.printStackTrace();
}
ipAddress = inet.getHostAddress();
}
}
// 对于通过多个代理的情况,第一个IP为客户端真实IP,多个IP按照','分割
// "***.***.***.***".length()
if (ipAddress != null && ipAddress.length() > 15) {
if (ipAddress.indexOf(SEPARATOR) > 0) {
ipAddress = ipAddress.substring(0, ipAddress.indexOf(","));
}
}
} catch (Exception e) {
ipAddress = "";
}
return ipAddress;
}
}
登录次数校验拦截器
下面实现的拦截器可以实现对某个接口的拦截,而非整个服务器的拦截。如果对整个服务器都进行拦截也是一样的思想。
对于访问次数校验的拦截器,包括前面的短信校验,又使用到了Redis工具类,对于Redis工具类的代码我也会放在文章下方。
在这个拦截器中,存在两个属性:
- interceptorUrl:指定要拦截的URL,用于保存用户不可访问的接口,这个URL和拦截器配置的拦截URL一致
- timeBound:规定的时间范围,单位:秒
- requestNum:规定时间内允许的的访问量阈值
实现思路就是每次访问的时候先判断redis有没有保存此IP访问该接口的记录,有的话累加次数,没有的话创建,并且初始化次数为1。当次数到达阈值,拦截此接口。
package cn.rayfoo.common.interceptor;
import cn.rayfoo.common.exception.MyException;
import cn.rayfoo.common.response.HttpStatus;
import cn.rayfoo.common.util.net.ClientUtil;
import cn.rayfoo.common.util.redis.RedisUtil;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @author rayfoo@qq.com
* @version 1.0
* @date 2020/8/6 11:49
* 登录次数校验拦截器
*/
@NoArgsConstructor
@Getter
@Slf4j
public class AccessInterceptor implements HandlerInterceptor {
@Autowired
private RedisUtil redisUtil;
/**
* 需要拦截的URL
*/
private String interceptorUrl;
/**
* 规定的时间范围 单位:秒
*/
private Long timeBound;
/**
* 规定时间内允许的的访问量阈值
*/
private Integer requestNum;
/**
* @param interceptorUrl 要拦截的URL
* @param timeBound 规定的时间范围
* @param requestNum 时间范围内的请求次数超过多少时禁止访问
*/
public AccessInterceptor(String interceptorUrl, Long timeBound, Integer requestNum) {
this.interceptorUrl = interceptorUrl;
this.timeBound = timeBound;
this.requestNum = requestNum;
}
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//判断该ip地址五分钟内注册了多少次 如果超过五次,将其ip列入黑名单十分钟。 每注册一次给此id加1
String ipAddr = ClientUtil.getIpAddr(request) + interceptorUrl;
//判断ip地址是否存在
if (redisUtil.hasKey(ipAddr)) {
//获取五分钟内获取验证码的次数
Integer count = (Integer) redisUtil.get(ipAddr);
//如果在x分钟内获取了超过x次
if (count > requestNum) {
//获取过期时间
long expire = redisUtil.getExpire(ipAddr);
log.error(ClientUtil.getIpAddr(request) + "对" + interceptorUrl + "操作频繁,此接口已将其暂时列入黑名单");
//告知用户限制时间还有多久
throw MyException.builder().code(HttpStatus.INTERNAL_SERVER_ERROR.value()).msg("您的操作过于频繁,请" + expire + "秒后重试!").build();
}
//不到五次就累加
redisUtil.incr(ipAddr, 1L);
} else {
//不存在的话 创建
redisUtil.set(ipAddr, 1L);
//过期时间设置为time秒
redisUtil.expire(ipAddr, timeBound);
}
return true;
}
}
拦截器注册
package cn.rayfoo.common.config;
import cn.rayfoo.common.interceptor.AccessInterceptor;
import cn.rayfoo.modules.base.interceptor.RegisterValidateInterceptor;
import cn.rayfoo.modules.base.interceptor.SMSValidateInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
* @author rayfoo@qq.com
* @version 1.0
* @date 2020/8/6 9:43
* @description 拦截器配置类
*/
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
/**
* 由于使用了其他依赖 将自定义的拦截器作为Bean写入配置
* @return
*/
@Bean
public SMSValidateInterceptor getSMSValidateInterceptor(){
return new SMSValidateInterceptor();
}
/**
* 短信接口的拦截器
* @return
*/
@Bean
public AccessInterceptor getCodeAccessInterceptor(){
return new AccessInterceptor("/user/code",300L,5);
}
/**
* 用户注册参数校验
* @return
*/
@Bean
public RegisterValidateInterceptor getRegisterValidateInterceptor(){
return new RegisterValidateInterceptor();
}
/**
* 注册拦截器
* @param registry
*/
@Override
public void addInterceptors(InterceptorRegistry registry) {
//注册短信验证码接口的请求次数拦截器
AccessInterceptor codeAccessInterceptor = getCodeAccessInterceptor();
registry.addInterceptor(codeAccessInterceptor)
.addPathPatterns(codeAccessInterceptor.getInterceptorUrl());
//注册手机号校验拦截器
registry.addInterceptor(getSMSValidateInterceptor())
.addPathPatterns("/user/code");
//用户注册参数校验 已经使用全局校验实现
// registry.addInterceptor(getRegisterValidateInterceptor())
// .addPathPatterns("/user/register");
}
}
Redis工具类
此工具类封装了大量的Redis操作,可以很方便的在Java中使用Redis,其包含两个抽象类,以下是它的完整代码:
package cn.rayfoo.common.util.redis;
import java.util.concurrent.TimeUnit;
/**
* @author rayfoo@qq.com
* @version 1.0
* @date 2020/8/3 21:37
*/
public abstract class BaseStatus {
/**
* 过期时间相关枚举
*/
public static enum ExpireEnum{
//未读消息的有效期为30天
UNREAD_MSG(30L, TimeUnit.DAYS)
;
/**
* 过期时间
*/
private Long time;
/**
* 时间单位
*/
private TimeUnit timeUnit;
ExpireEnum(Long time, TimeUnit timeUnit) {
this.time = time;
this.timeUnit = timeUnit;
}
public Long getTime() {
return time;
}
public TimeUnit getTimeUnit() {
return timeUnit;
}
}
}
package cn.rayfoo.common.util.redis;
import java.util.concurrent.TimeUnit;
/**
* @author rayfoo@qq.com
* @version 1.0
* @date 2020/8/3 21:37
*/
public abstract class Status {
/**
* 过期时间相关枚举
*/
public static enum ExpireEnum{
//未读消息的有效期为30天
UNREAD_MSG(30L, TimeUnit.DAYS)
;
/**
* 过期时间
*/
private Long time;
/**
* 时间单位
*/
private TimeUnit timeUnit;
ExpireEnum(Long time, TimeUnit timeUnit) {
this.time = time;
this.timeUnit = timeUnit;
}
public Long getTime() {
return time;
}
public TimeUnit getTimeUnit() {
return timeUnit;
}
}
}
package cn.rayfoo.common.util.redis;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.BoundListOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
/**
* @author rayfoo@qq.com
* @version 1.0
* @date 2020/8/3 21:34
*/
@Component
public class RedisUtil {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
public RedisUtil(RedisTemplate<String, Object> redisTemplate) {
this.redisTemplate = redisTemplate;
}
/**
* 指定缓存失效时间
*
* @param key 键
* @param time 时间(秒)
* @return
*/
public boolean expire(String key, long time) {
try {
if (time > 0) {
redisTemplate.expire(key, time, TimeUnit.SECONDS);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 根据key 获取过期时间
*
* @param key 键 不能为null
* @return 时间(秒) 返回0代表为永久有效
*/
public long getExpire(String key) {
return redisTemplate.getExpire(key, TimeUnit.SECONDS);
}
/**
* 判断key是否存在
*
* @param key 键
* @return true 存在 false不存在
*/
public boolean hasKey(String key) {
try {
return redisTemplate.hasKey(key);
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 删除缓存
*
* @param key 可以传一个值 或多个
*/
@SuppressWarnings("unchecked")
public void del(String... key) {
if (key != null && key.length > 0) {
if (key.length == 1) {
redisTemplate.delete(key[0]);
} else {
redisTemplate.delete(CollectionUtils.arrayToList(key));
}
}
}
//============================String=============================
/**
* 普通缓存获取
*
* @param key 键
* @return 值
*/
public Object get(String key) {
return key == null ? null : redisTemplate.opsForValue().get(key);
}
/**
* 普通缓存放入
*
* @param key 键
* @param value 值
* @return true成功 false失败
*/
public boolean set(String key, Object value) {
try {
redisTemplate.opsForValue().set(key, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 普通缓存放入并设置时间
*
* @param key 键
* @param value 值
* @param time 时间(秒) time要大于0 如果time小于等于0 将设置无限期
* @return true成功 false 失败
*/
public boolean set(String key, Object value, long time) {
try {
if (time > 0) {
redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
} else {
set(key, value);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 递增
*
* @param key 键
* @param delta 要增加几(大于0)
* @return
*/
public long incr(String key, long delta) {
if (delta < 0) {
throw new RuntimeException("递增因子必须大于0");
}
return redisTemplate.opsForValue().increment(key, delta);
}
/**
* 递减
*
* @param key 键
* @param delta 要减少几(小于0)
* @return
*/
public long decr(String key, long delta) {
if (delta < 0) {
throw new RuntimeException("递减因子必须大于0");
}
return redisTemplate.opsForValue().increment(key, -delta);
}
//================================Map=================================
/**
* HashGet
*
* @param key 键 不能为null
* @param item 项 不能为null
* @return 值
*/
public Object hget(String key, String item) {
return redisTemplate.opsForHash().get(key, item);
}
/**
* 获取hashKey对应的所有键值
*
* @param key 键
* @return 对应的多个键值
*/
public Map<Object, Object> hmget(String key) {
return redisTemplate.opsForHash().entries(key);
}
/**
* HashSet
*
* @param key 键
* @param map 对应多个键值
* @return true 成功 false 失败
*/
public boolean hmset(String key, Map<String, Object> map) {
try {
redisTemplate.opsForHash().putAll(key, map);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* HashSet 并设置时间
*
* @param key 键
* @param map 对应多个键值
* @param time 时间(秒)
* @return true成功 false失败
*/
public boolean hmset(String key, Map<String, Object> map, long time) {
try {
redisTemplate.opsForHash().putAll(key, map);
if (time > 0) {
expire(key, time);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 向一张hash表中放入数据,如果不存在将创建
*
* @param key 键
* @param item 项
* @param value 值
* @return true 成功 false失败
*/
public boolean hset(String key, String item, Object value) {
try {
redisTemplate.opsForHash().put(key, item, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 向一张hash表中放入数据,如果不存在将创建
*
* @param key 键
* @param item 项
* @param value 值
* @param time 时间(秒) 注意:如果已存在的hash表有时间,这里将会替换原有的时间
* @return true 成功 false失败
*/
public boolean hset(String key, String item, Object value, long time) {
try {
redisTemplate.opsForHash().put(key, item, value);
if (time > 0) {
expire(key, time);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 删除hash表中的值
*
* @param key 键 不能为null
* @param item 项 可以使多个 不能为null
*/
public void hdel(String key, Object... item) {
redisTemplate.opsForHash().delete(key, item);
}
/**
* 判断hash表中是否有该项的值
*
* @param key 键 不能为null
* @param item 项 不能为null
* @return true 存在 false不存在
*/
public boolean hHasKey(String key, String item) {
return redisTemplate.opsForHash().hasKey(key, item);
}
/**
* hash递增 如果不存在,就会创建一个 并把新增后的值返回
*
* @param key 键
* @param item 项
* @param by 要增加几(大于0)
* @return
*/
public double hincr(String key, String item, double by) {
return redisTemplate.opsForHash().increment(key, item, by);
}
/**
* hash递减
*
* @param key 键
* @param item 项
* @param by 要减少记(小于0)
* @return
*/
public double hdecr(String key, String item, double by) {
return redisTemplate.opsForHash().increment(key, item, -by);
}
//============================set=============================
/**
* 根据key获取Set中的所有值
*
* @param key 键
* @return
*/
public Set<Object> sGet(String key) {
try {
return redisTemplate.opsForSet().members(key);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 根据value从一个set中查询,是否存在
*
* @param key 键
* @param value 值
* @return true 存在 false不存在
*/
public boolean sHasKey(String key, Object value) {
try {
return redisTemplate.opsForSet().isMember(key, value);
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 将数据放入set缓存
*
* @param key 键
* @param values 值 可以是多个
* @return 成功个数
*/
public long sSet(String key, Object... values) {
try {
return redisTemplate.opsForSet().add(key, values);
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
/**
* 将set数据放入缓存
*
* @param key 键
* @param time 时间(秒)
* @param values 值 可以是多个
* @return 成功个数
*/
public long sSetAndTime(String key, long time, Object... values) {
try {
Long count = redisTemplate.opsForSet().add(key, values);
if (time > 0) {
expire(key, time);
}
return count;
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
/**
* 获取set缓存的长度
*
* @param key 键
* @return
*/
public long sGetSetSize(String key) {
try {
return redisTemplate.opsForSet().size(key);
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
/**
* 移除值为value的
*
* @param key 键
* @param values 值 可以是多个
* @return 移除的个数
*/
public long setRemove(String key, Object... values) {
try {
Long count = redisTemplate.opsForSet().remove(key, values);
return count;
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
//===============================list=================================
/**
* 获取list缓存的内容
*
* @param key 键
* @param start 开始
* @param end 结束 0 到 -1代表所有值
* @return
*/
public List<Object> lGet(String key, long start, long end) {
try {
return redisTemplate.opsForList().range(key, start, end);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 获取list缓存的长度
*
* @param key 键
* @return
*/
public long lGetListSize(String key) {
try {
return redisTemplate.opsForList().size(key);
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
/**
* 通过索引 获取list中的值
*
* @param key 键
* @param index 索引 index>=0时, 0 表头,1 第二个元素,依次类推;index<0时,-1,表尾,-2倒数第二个元素,依次类推
* @return
*/
public Object lGetIndex(String key, long index) {
try {
return redisTemplate.opsForList().index(key, index);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 将list放入缓存
*
* @param key 键
* @param value 值
* @return
*/
public boolean lSet(String key, Object value) {
try {
redisTemplate.opsForList().rightPush(key, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 将list放入缓存
*
* @param key 键
* @param value 值
* @param time 时间(秒)
* @return
*/
public boolean lSet(String key, Object value, long time) {
try {
redisTemplate.opsForList().rightPush(key, value);
if (time > 0) {
expire(key, time);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 将list放入缓存
*
* @param key 键
* @param value 值
* @return
*/
public boolean lSet(String key, List<Object> value) {
try {
redisTemplate.opsForList().rightPushAll(key, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 将list放入缓存
*
* @param key 键
* @param value 值
* @param time 时间(秒)
* @return
*/
public boolean lSet(String key, List<Object> value, long time) {
try {
redisTemplate.opsForList().rightPushAll(key, value);
if (time > 0) {
expire(key, time);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 根据索引修改list中的某条数据
*
* @param key 键
* @param index 索引
* @param value 值
* @return
*/
public boolean lUpdateIndex(String key, long index, Object value) {
try {
redisTemplate.opsForList().set(key, index, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 移除N个值为value
*
* @param key 键
* @param count 移除多少个
* @param value 值
* @return 移除的个数
*/
public long lRemove(String key, long count, Object value) {
try {
Long remove = redisTemplate.opsForList().remove(key, count, value);
return remove;
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
/**
* 模糊查询获取key值
*
* @param pattern
* @return
*/
public Set keys(String pattern) {
return redisTemplate.keys(pattern);
}
/**
* 使用Redis的消息队列
*
* @param channel
* @param message 消息内容
*/
public void convertAndSend(String channel, Object message) {
redisTemplate.convertAndSend(channel, message);
}
//=========BoundListOperations 用法 start============
/**
* 将数据添加到Redis的list中(从右边添加)
*
* @param listKey
* @param expireEnum 有效期的枚举类
* @param values 待添加的数据
*/
public void addToListRight(String listKey, BaseStatus.ExpireEnum expireEnum, Object... values) {
//绑定操作
BoundListOperations<String, Object> boundValueOperations = redisTemplate.boundListOps(listKey);
//插入数据
boundValueOperations.rightPushAll(values);
//设置过期时间
boundValueOperations.expire(expireEnum.getTime(), expireEnum.getTimeUnit());
}
/**
* 根据起始结束序号遍历Redis中的list
*
* @param listKey
* @param start 起始序号
* @param end 结束序号
* @return
*/
public List<Object> rangeList(String listKey, long start, long end) {
//绑定操作
BoundListOperations<String, Object> boundValueOperations = redisTemplate.boundListOps(listKey);
//查询数据
return boundValueOperations.range(start, end);
}
/**
* 弹出右边的值 --- 并且移除这个值
*
* @param listKey
*/
public Object rifhtPop(String listKey) {
//绑定操作
BoundListOperations<String, Object> boundValueOperations = redisTemplate.boundListOps(listKey);
return boundValueOperations.rightPop();
}
}