該例子需要用到 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
快速刷新多次后出現: