前言
针对有人恶意频繁访问接口,对服务器造成巨大压力
使用自定义注解方式进行拦截
SpringBoot + Redis方式进行处理拦截请求
依赖引入
<!-- redis 缓存 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- pool 对象池 -->
<!-- spring2.X集成redis所需common-pool2-->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
YML文件
# 开发环境配置
server:
port: 8080
spring:
# redis 配置
redis:
# 地址
host: 127.0.0.1
# 端口,默认为6379
port: 6379
# 数据库索引
database: 0
# 密码默认为空
password:
# 连接超时时间
timeout: 10s
lettuce:
pool:
# 连接池中的最小空闲连接
min-idle: 0
# 连接池中的最大空闲连接
max-idle: 8
# 连接池的最大数据库连接数
max-active: 8
# #连接池最大阻塞等待时间(使用负值表示没有限制)
max-wait: -1ms
Redis自定义序列化
防止存Redis的时候key或value乱码
@EnableCaching //开启缓存
@Configuration //配置类
public class RedisConfig extends CachingConfigurerSupport {
@Bean
@SuppressWarnings("all")
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
RedisTemplate<String, Object> template = new RedisTemplate<String, Object>();
template.setConnectionFactory(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);
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
// key采用String的序列化方式
template.setKeySerializer(stringRedisSerializer);
// hash的key也采用String的序列化方式
template.setHashKeySerializer(stringRedisSerializer);
// value序列化方式采用jackson
template.setValueSerializer(jackson2JsonRedisSerializer);
// hash的value序列化方式采用jackson
template.setHashValueSerializer(jackson2JsonRedisSerializer);
template.afterPropertiesSet();
return template;
}
}
创建自定义注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface AccessLimit {
/**
* 时间限制
*
* @return
*/
int seconds();
/**
* 请求次数
*
* @return
*/
int maxCount();
}
拦截恶意访问--逻辑处理
package com.zz.framework.aop.aspect;
import com.alibaba.fastjson.JSON;
import com.zz.framework.aop.annotation.AccessLimit;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.concurrent.TimeUnit;
@Component
public class AccessLimItInterceptor implements HandlerInterceptor {
@Autowired
private RedisTemplate redisTemplate;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 判断请求是否属于方法的请求
if (handler instanceof HandlerMethod) {
HandlerMethod hm = (HandlerMethod) handler;
// 获取方法中的注解,看是否有该注解
AccessLimit accessLimit = hm.getMethodAnnotation(AccessLimit.class);
if (null == accessLimit) {
return true;
}
int seconds = accessLimit.seconds();
int maxCount = accessLimit.maxCount();
String ip = request.getRemoteAddr();
String key = request.getServletPath() + ":" + ip;
// 从redis中获取用户访问的次数
Integer count = (Integer) redisTemplate.opsForValue().get(key);
if (null == count || -1 == count) {
// 第一次访问
redisTemplate.opsForValue().set(key, 1, seconds, TimeUnit.SECONDS);
return true;
}
if (count < maxCount) {
// 加1
count = count + 1;
redisTemplate.opsForValue().set(key, count, 0);
return true;
}
// 超出访问次数
if (count >= maxCount) {
// response 返回 json 请求过于频繁请稍后再试
response.setCharacterEncoding("UTF-8");
response.setContentType("application/json; charset=utf-8");
HashMap<String, Object> map = new HashMap<>();
map.put("code",9999);
map.put("msg","操作过于频繁!!!");
response.getWriter().write(JSON.toJSONString(map));
return false;
}
}
return true;
}
}
拦截器注入容器管理
@Configuration
public class WebMvcConfig extends WebMvcConfigurerAdapter {
@Autowired
private AccessLimItInterceptor accessLimItInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(accessLimItInterceptor);
super.addInterceptors(registry);
}
}
测试接口
@RequestMapping("/test")
@AccessLimit(seconds = 30,maxCount = 5) // 30秒内,允许请求5次
public String test() {
return "Hello Word";
}