package cn.qfei.astrict;
import java.lang.annotation.*;
/**
* @Author:changg
* @Date:2023/3/16 13:48
* @Filename:AccessLimit
*/
@Documented
@Inherited
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AccessLimit {
/**
* 在 seconds 秒内 , 最大只能请求 maxCount 次
*
* @return
*/
// 秒
int seconds() default 1;
// 最大数量
int maxCount() default 1;
}
package cn.qfei.astrict;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import java.io.Serializable;
/**
* @Author:changg
* @Date:2023/3/16 13:51
* @Filename:WebConfig
*/
@Configuration
public class WebConfig extends WebMvcConfigurerAdapter {
@Autowired
private FangshuaInterceptor interceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(interceptor);
}
/**
* 解决 redis 的编码统一方面的问题 (了解)
*
* @param connectionFactory
* @return
*/
@Bean
public RedisTemplate<String, Serializable> redisTemplate(LettuceConnectionFactory connectionFactory) {
//创建 redisTemplate 模版
RedisTemplate<String, Serializable> redisTemplate = new RedisTemplate<>();
//设置 value 的转化格式和 key 的转化格式
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
//关联 redisConnectionFactory
redisTemplate.setConnectionFactory(connectionFactory);
return redisTemplate;
}
}
package cn.qfei.astrict;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.OutputStream;
import java.util.concurrent.TimeUnit;
/**
* @Author:changg
* @Date:2023/3/16 13:49
* @Filename:FangshuaInterceptor
*/
@Component
public class FangshuaInterceptor extends HandlerInterceptorAdapter {
@Resource
RedisTemplate<String, Object> redisTemplate;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 1. 判断请求是否属于方法的请求
if (handler instanceof HandlerMethod) {
// 2. 取出当前方法的对象
HandlerMethod handler1 = (HandlerMethod) handler;
// 3. 获取方法中的注解,看是否有该注解
AccessLimit accessLimit = handler1.getMethodAnnotation(AccessLimit.class);
// 3.1 : 不包含此注解,则不进行操作
if (accessLimit != null) {
// 3.2 : 判断请求是否受限制
if (isLimit(request, accessLimit)) {
render(response, "{\"code\":\"30001\",\"message\":\"请求过快\"}");
return false;
}
}
}
return true;
}
//判断请求是否受限
public boolean isLimit(HttpServletRequest request, AccessLimit accessLimit) {
// 受限的redis 缓存key ,因为这里用浏览器做测试,我就用sessionid 来做唯一key,如果是app ,可以使用 用户ID 之类的唯一标识。
String limitKey = request.getServletPath() + request.getSession().getId();
// 从缓存中获取,当前这个请求访问了几次
Integer redisCount = (Integer) redisTemplate.opsForValue().get(limitKey);
if (redisCount == null) {
//初始 次数
redisTemplate.opsForValue().set(limitKey, 1, accessLimit.seconds(), TimeUnit.SECONDS);
System.out.println("写入redis --");
} else {
System.out.println("intValue-->" + redisCount.intValue());
if (redisCount.intValue() >= accessLimit.maxCount()) {
return true;
}
// 次数自增
redisTemplate.opsForValue().increment(limitKey);
}
return false;
}
private void render(HttpServletResponse response, String cm) throws Exception {
response.setContentType("application/json;charset=UTF-8");
OutputStream out = response.getOutputStream();
out.write(cm.getBytes("UTF-8"));
out.flush();
out.close();
}
}
@AccessLimit(seconds = 20, maxCount = 2)