springboot 自定义拦截器 防止恶意请求
该例子需要用到 redis
在applocation.properties中加入redis的配置信息
server.port=8030
# Redis数据库索引(默认为0)
spring.redis.database=0
# Redis服务器地址
spring.redis.host=localhost
# Redis服务器连接端口
spring.redis.port=6379
# Redis服务器连接密码(默认为空)
spring.redis.password=
#连接池最大连接数(使用负值表示没有限制)
spring.redis.jedis.pool.max-idle=8
# 连接池最大阻塞等待时间(使用负值表示没有限制)
spring.redis.jedis.pool.max-wait=
# 连接池中的最大空闲连接
spring.redis.jedis..pool.max-idle=8
# 连接池中的最小空闲连接
spring.redis.jedis.pool.min-idle=0
# 连接超时时间(毫秒)
spring.redis.timeout=300
RedisConfig.java
package cn.rc.config; import org.springframework.cache.annotation.CachingConfigurerSupport; import org.springframework.cache.annotation.EnableCaching; 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; import com.fasterxml.jackson.annotation.JsonAutoDetect; import com.fasterxml.jackson.annotation.PropertyAccessor; import com.fasterxml.jackson.databind.ObjectMapper; @Configuration @EnableCaching // 开启注解 public class RedisConfig extends CachingConfigurerSupport { @Bean public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) { RedisTemplate<String, Object> template = new RedisTemplate<>(); // 配置连接工厂 template.setConnectionFactory(factory); // 使用Jackson2JsonRedisSerializer来序列化和反序列化redis的value值(默认使用JDK的序列化方式) Jackson2JsonRedisSerializer jacksonSeial = new Jackson2JsonRedisSerializer(Object.class); ObjectMapper om = new ObjectMapper(); // 指定要序列化的域,field,get和set,以及修饰符范围,ANY是都有包括private和public om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); // 指定序列化输入的类型,类必须是非final修饰的,final修饰的类,比如String,Integer等会跑出异常 om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); jacksonSeial.setObjectMapper(om); // 值采用json序列化 template.setValueSerializer(jacksonSeial); // 使用StringRedisSerializer来序列化和反序列化redis的key值 template.setKeySerializer(new StringRedisSerializer()); // 设置hash key 和value序列化模式 template.setHashKeySerializer(new StringRedisSerializer()); template.setHashValueSerializer(jacksonSeial); template.afterPropertiesSet(); return template; } }
需要先启动redis功能
一、声明一个自定义的注解类
AccessLimit.java
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface AccessLimit { int seconds(); int maxCount(); boolean needLogin() default true; }
二、声明一个自定义的拦截器:
AccessLimtInterceptor.java
@Component public class AccessLimtInterceptor 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(); boolean needLogin = accessLimit.needLogin(); if (needLogin) { //判断是否登录 } String ip=request.getRemoteAddr(); String key = request.getServletPath() + ":" + ip ; 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) { 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"); JsonResponse result = new JsonResponse<>(); result.setCode(9999); result.setMessage("操作过于频繁"); Object obj = JSONObject.toJSON(result); response.getWriter().write(JSONObject.toJSONString(obj)); return false; } } return true; } }
将 拦截器 注册到容器:WebMvcConfig.java
/** * MVC 设置 * */ @Configuration public class WebMvcConfig extends WebMvcConfigurerAdapter { @Autowired private AccessLimtInterceptor accessLimtInterceptor; @Bean public AccessTokenVerifyInterceptor tokenVerifyInterceptor() { return new AccessTokenVerifyInterceptor(); } @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(accessLimtInterceptor); registry.addInterceptor(tokenVerifyInterceptor()).addPathPatterns("/**"); super.addInterceptors(registry); } }
进行测试接口:
使用 @AccessLimit(seconds = 15, maxCount = 3) 注解 修饰接口 , 15秒 只允许访问3次
@RestController @RequestMapping("test") public class TestAccessLimitController { @GetMapping("accessLimit") @AccessLimit(seconds = 15, maxCount = 3) //15秒内 允许请求3次 public String testAccessLimit() { return "success"; } }
浏览器请求:http://localhost:8030/test/accessLimit
返回: success
快速刷新多次后出现: