首页 > 数据库 >Springboot2.x 结合 redis 实现ip请求次数限制

Springboot2.x 结合 redis 实现ip请求次数限制

时间:2022-11-06 11:57:16浏览次数:84  
标签:spring org redis request springframework Springboot2 ip import

参考

  1. https://cloud.tencent.com/developer/article/1607647 Spring Boot整合Redis代码详解,四步搞定!
  2. https://blog.csdn.net/jinyangbest/article/details/98205802 springboot 拦截器@Autowired 注入失败
  3. https://www.cnblogs.com/harriets-zhang/p/14517312.html SpringBoot配置RedisTemplate
  4. https://blog.csdn.net/jinyangbest/article/details/98205802 springboot 拦截器@Autowired 注入失败
  5. https://redis.com.cn/redis-installation.html 在 windows 上安装 Redis
  6. https://www.cnblogs.com/xiaqiuchu/p/15106720.html Springboot2.3.5 实现JWT授权验证并针对不同用户实现多个拦截

环境

环境 版本 操作
windows 10
JDK 11
Springboot 2.3.7.RELEASE
spring-boot-starter-data-redis
redis x64-3.0.504 免安装版 下载

实现功能

根据用户ip,每个url接口每60秒只能请求2次。

遇到的问题

可能是好久没写java,忘记了springboot加载流程。导致 redis 拦截器注入失败

正文

1. windows 安装 redis

2. pom引入依赖

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

3. application.properties 添加配置

# redis 配置
# https://zhuanlan.zhihu.com/p/139528556
#spring.redis.host=127.0.0.1
#spring.redis.port=6379
##spring.redis.password=
#spring.redis.database=0
#spring.redis.timeout=5000
#spring.redis.jedis.pool.max-active=100
#spring.redis.jedis.pool.max-idle=10
#spring.redis.jedis.pool.max-wait=100000

############################################################
# REDIS 配置 https://cloud.tencent.com/developer/article/1607647
############################################################
spring.redis.database=0
spring.redis.host=127.0.0.1
spring.redis.port=6379
spring.redis.password=
spring.redis.jedis.pool.max-active=8
spring.redis.jedis.pool.max-wait=-1
spring.redis.jedis.pool.max-idle=10
spring.redis.jedis.pool.min-idle=2
spring.redis.timeout=6000


# 请求ip限流限制
# 一分钟内只能请求 60 次
# 请求数量
request.limit.count=2
# 请求时间
request.limit.seconds=60

4. 创建 RedisConfig


import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

/**
 * 解决redis 拦截器注入失败问题
 * https://blog.csdn.net/jinyangbest/article/details/98205802
 * redis配置
 * https://www.cnblogs.com/harriets-zhang/p/14517312.html
 */
@Configuration
public class RedisConfig {

    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();

        template.setConnectionFactory(redisConnectionFactory);

        //配置序列化方式
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);

        ObjectMapper obm=new ObjectMapper();
        // 指定要序列化的域,field,get和set,以及修饰符范围,ANY是都有包括private和publi
        obm.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        // 指定序列化输入的类型,类必须是非final修饰的,final修饰的类,比如String,Integer等会跑出异常
        obm.activateDefaultTyping(LaissezFaireSubTypeValidator.instance,ObjectMapper.DefaultTyping.NON_FINAL);

        jackson2JsonRedisSerializer.setObjectMapper(obm);

        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
        //key 采用String的序列化方式
        template.setKeySerializer(stringRedisSerializer);
        //hash
        template.setHashKeySerializer(stringRedisSerializer);
        //value
        template.setValueSerializer(jackson2JsonRedisSerializer);
        template.afterPropertiesSet();
        return template;
    }

}

5. 自定义拦截器异常

import lombok.Data;
import org.springframework.http.HttpStatus;

/**
 * @author xiaqiuchu
 */
@Data
public class BaseException extends RuntimeException{
    public HttpStatus httpCode = 404;
    BaseException(String msg){
        super(msg);
    }
}

/**
 * 请求超出次数限制
 */
public class RequestLimitException extends BaseException{
    /**
     * 请求超出次数限制
     */
    public RequestLimitException(){
        super("请求超出次数限制");
    }
}

6. 请求限流拦截器处理类


import cn.hutool.extra.servlet.ServletUtil;
import com.xxx.xxx.exception.RequestLimitException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

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

/**
 * 请求限制拦截器
 * 结合 redis https://cloud.tencent.com/developer/article/1607647
 */
// 注意,这里不需要加 @Component 注解,在IntercaptorConfig手动注册bean
@Slf4j
public class RequestLimitInterceptor extends HandlerInterceptorAdapter {

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    /**
     * 次数
     */
    @Value("${request.limit.count}")
    private Integer requestLimitCount;

    /**
     * 秒
     */
    @Value("${request.limit.seconds}")
    private Integer requestLimitSeconds;

    @Override
    public  boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        log.info("进入RequestLimitInterceptor拦截器了");
        Integer num = this.getCurrentIpRequestNum(request) + 1;
        this.updateCurrentIpRequestNum(request, num);
        return true;

    }



    /**
     * 获取当前用户的请求数
     * @param request
     * @return
     */
    private Integer getCurrentIpRequestNum(HttpServletRequest request){
        String currentUserCacheKey = request.getRequestURL() + ServletUtil.getClientIP(request, null);
        Integer num = (Integer) redisTemplate.opsForValue().get(currentUserCacheKey);
        if(num == null){
            return 0;
        }
        return num;
    }
    /**
     * 更新当前用户的请求数
     *
     * @param request
     * @return
     */
    private void updateCurrentIpRequestNum(HttpServletRequest request, Integer newNum){
        if(newNum > requestLimitCount){
            throw new RequestLimitException();
        }
        String currentUserCacheKey = request.getRequestURL() + ServletUtil.getClientIP(request, null);
        log.info("当前IP:{},请求URL:{},请求次数:{},请求限制:{}", ServletUtil.getClientIP(request, null), request.getRequestURL(), newNum, requestLimitCount);
        redisTemplate.opsForValue().set(currentUserCacheKey, newNum, requestLimitSeconds, TimeUnit.SECONDS);
    }
}

7. 拦截器配置类(注册拦截器)

// 引入拦截器逻辑处理类
import com.xxx.xxx.interceptor.RequestLimitInterceptor;
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;

/**
 * 拦截器配置类
 * https://www.cnblogs.com/xiaqiuchu/p/15106720.html
 */
@Configuration
public class IntercaptorConfig  implements WebMvcConfigurer {

    /**
     * 解决拦截器注入问题,注入为null
     * https://blog.csdn.net/jinyangbest/article/details/98205802
     * @return
     */
    @Bean
    public RequestLimitInterceptor requestLimitInterceptor(){
        return new RequestLimitInterceptor();
    }

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        /**
         * 请求限制拦截器
         */
        registry.addInterceptor(requestLimitInterceptor())
                /**
                 * 拦截的路径
                 */
                .addPathPatterns("/**");
    }

标签:spring,org,redis,request,springframework,Springboot2,ip,import
From: https://www.cnblogs.com/xiaqiuchu/p/16862289.html

相关文章

  • Linux安装JDK,解压报错:gzip: stdin: not in gzip format
    问题描述在Linux安装JDKtar-zxvf命令解压时,报错,无法解压报错内容:gzip:stdin:notingzipformattar:Childreturnedstatus1tar:Errorisnotrecoverable:e......
  • NOIP 字串变换
    思路因为有很多的相似部分不妨用define定义一下,会很好.Code#include<bits/stdc++.h>usingnamespacestd;#defineMAXN32005#defineF(i,a,b)for(inti=a;i......
  • NOIP 时间复杂度
    思路写出如下的伪代码:然后实现出来就是这样:#include<bits/stdc++.h>usingnamespacestd;#defineMAXN32005#defineF(i,a,b)for(inti=a;i<=b;i++)#defi......
  • Windows版的Redis安装
    到官网下载:​​​https://github.com/MicrosoftArchive/redis/releases​​​如果没有到如下网盘下载:​更改配置文件​Windows版的Redis有2个配置文件,一个是:redis.windows.c......
  • Makefile.win recipe for target '项目1.exe' failed
    在运行代码的时候出现了这个问题,查阅了许多资料,有的说是编译器的问题,有的说是重复定义变量名称的问题,在对代码检查后发现不是这两者的问题是我前面数组定义有问题,将数组定义......
  • 【lwip】10-ICMP协议&源码分析
    目录前言10.1ICMP简介10.2ICMP报文10.2.1ICMP报文格式10.2.2ICMP报文类型10.2.3ICMP报文固定首部字段意义10.3ICMP差错报告报文10.3.1目的不可达10.3.2源站抑制10.......
  • NOIP2017 逛公园 记忆化搜索|dp(已过hack数据)
    30pts可以发现,\(k=0\)的情况下,问题转化为最短路计数,即从起点\(s\)到每个点有多少最短路。跑最短路的时候顺便维护\(ans[u]\),表示从\(s\)到\(u\)的最短路方案,讨论如下:①......
  • 使用applescript 触发键盘快捷键
    https://stackoverflow.com/questions/3690167/how-can-one-invoke-a-keyboard-shortcut-from-within-an-applescript如果想触发tab的话,就把引号去掉,比如这样来触发comma......
  • 微服务Spring Boot 整合Redis 基于Redis的Stream 消息队列 实现异步秒杀下单
    文章目录​​一、什么是Redis消息队列?​​​​二、Redis消息队列--基于RedisList实现消息队列​​​​三、Redis消息队列--基于Pubsub的消息队列​​​​四、......
  • MySQL read_ only Read Only Attribute Description
    一、MySQLread_onlyReadOnlyAttributeDescription在MySQL数据库中,在进行数据迁移和从库只读状态设置时,都会涉及到只读状态和Master-Slave主从关系设置,以下针对rea......