参考
- https://cloud.tencent.com/developer/article/1607647 Spring Boot整合Redis代码详解,四步搞定!
- https://blog.csdn.net/jinyangbest/article/details/98205802 springboot 拦截器@Autowired 注入失败
- https://www.cnblogs.com/harriets-zhang/p/14517312.html SpringBoot配置RedisTemplate
- https://blog.csdn.net/jinyangbest/article/details/98205802 springboot 拦截器@Autowired 注入失败
- https://redis.com.cn/redis-installation.html 在 windows 上安装 Redis
- 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