赞
踩
<dependency> <groupId>org.springframework.data</groupId> <artifactId>spring-data-redis</artifactId> <version>2.3.6.RELEASE</version> </dependency> <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- https://mvnrepository.com/artifact/com.alibaba/fastjson --> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.41</version> </dependency>
spring: redis: database: 0 host: 127.0.0.1 port: 6379 password: "0211" # 连接超时时间(ms) timeout: 5000 # 高版本springboot中使用jedis或者lettuce jedis: pool: # 连接池最大连接数(负值表示无限制) max-active: 8 # 连接池最大阻塞等待时间(负值无限制) max-wait: 5000 # 最大空闲链接数 max-idle: 8 # 最小空闲链接数 min-idle: 1 server: port: 8888
@Configuration public class RedisConfig { private static Logger logger = LoggerFactory.getLogger(RedisConfig.class); @Value("${spring.redis.host}") private String host; @Value("${spring.redis.password}") private String password; @Value("${spring.redis.port}") private int port; @Value("${spring.redis.database}") private int database; @SuppressWarnings("all") @Bean public StringRedisTemplate redisTemplate(RedisConnectionFactory factory){ StringRedisTemplate template = new StringRedisTemplate(factory); Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class); ObjectMapper om = new ObjectMapper(); om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); jackson2JsonRedisSerializer.setObjectMapper(om); RedisSerializer stringSerializer = new StringRedisSerializer(); template.setKeySerializer(stringSerializer); template.setValueSerializer(jackson2JsonRedisSerializer); template.setHashKeySerializer(stringSerializer); template.setHashValueSerializer(jackson2JsonRedisSerializer); template.afterPropertiesSet(); return template; } @Bean public JedisConnectionFactory jedisConnectionFactory() { logger.info("jedisConnectionFactory:初始化了"); RedisStandaloneConfiguration configuration = new RedisStandaloneConfiguration(); configuration.setHostName(host); configuration.setPassword(RedisPassword.of(password)); configuration.setPort(port); configuration.setDatabase(database); return new JedisConnectionFactory(configuration); } }
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface CounterLimit { /** * 调用方唯一key的名字 * * @return */ String name(); /** * 限制访问次数 * @return */ int limitTimes(); /** * 限制时长,也就是计数器的过期时间 * * @return */ long timeout(); /** * 限制时长单位 * * @return */ TimeUnit timeUnit(); }
@Component @Slf4j public class CounterLimiterHandlerInterceptor implements HandlerInterceptor { @Autowired private RedisTemplate redisTemplate; @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { if (handler instanceof HandlerMethod) { HandlerMethod handlerMethod = (HandlerMethod) handler; //判断方法是否包含CounterLimit,有这个注解就需要进行限速操作 if (handlerMethod.hasMethodAnnotation(CounterLimit.class)) { CounterLimit annotation = handlerMethod.getMethod().getAnnotation(CounterLimit.class); JSONObject result = new JSONObject(); String token = request.getParameter(annotation.name()); response.setContentType("text/json;charset=utf-8"); result.put("timestamp", System.currentTimeMillis()); BoundValueOperations<String, Integer> boundGeoOperations = redisTemplate.boundValueOps(token); // 如果用户身份唯一key为空,直接返回错误 if (StringUtils.isEmpty(token)) { result.put("result", "token is invalid"); response.getWriter().print(JSON.toJSONString(result)); // 如果限速校验通过,则将请求放行 } else if (checkLimiter(token, annotation)) { result.put("result", "请求成功"); Long expire = boundGeoOperations.getExpire(); log.info("result:{}, expire: {}", result, expire); return true; // 否则告知调用方达到限速上线 } else { result.put("result", "达到访问次数限制,禁止访问"); Long expire = boundGeoOperations.getExpire(); log.info("result:{}, expire: {}", result, expire); response.getWriter().print(JSON.toJSONString(result)); } return false; } } return true; } /** * 限速校验 */ private Boolean checkLimiter(String token, CounterLimit annotation) { BoundValueOperations<String, Integer> boundGeoOperations = redisTemplate.boundValueOps(token); Integer count = boundGeoOperations.get(); if (Objects.isNull(count)) { redisTemplate.boundValueOps(token).set(1, annotation.timeout(), annotation.timeUnit()); } else if (count >= annotation.limitTimes()) { return Boolean.FALSE; } else { redisTemplate.boundValueOps(token).set(count + 1, boundGeoOperations.getExpire(), annotation.timeUnit()); } return Boolean.TRUE; } }
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Autowired
private CounterLimiterHandlerInterceptor counterLimiterHandlerInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
//计数器限速
registry.addInterceptor(counterLimiterHandlerInterceptor).addPathPatterns("/**");
WebMvcConfigurer.super.addInterceptors(registry);
}
}
@RestController
public class CounterController {
@CounterLimit(name = "token", limitTimes = 5, timeout = 60, timeUnit = TimeUnit.SECONDS)
@GetMapping("/limit/counter")
public Object counterLimiter(String name, String token) {
JSONObject result = new JSONObject();
result.put("data", "success");
return result;
}
}
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。