Spring Boot過濾非法請求


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();
    }
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM