需求:
上传文件接口限制,限制每位用户十分钟只能上传10次
思路:
要限制用户调用接口的次数,可以使用Java的缓存库实现计数器,记录用户每次访问接口的时间和次数,并在达到限制后拒绝进一步的访问,拦截器可以使用AOP,进行切面拦截。
实现:
使用Google Guava库中的CacheBuilder类创建一个缓存对象。该缓存对象最多可以缓存1000个记录,并在10分钟后自动过期。然后提供一个isLimitExceeded方法,用于检查特定用户是否已超过允许的最大请求数。如果该用户的请求数已经超过了最大请求数,则返回true,否则返回false。在每次请求之后,该代码将用户的请求次数递增,并将其放入缓存中,以便下一次请求时可以访问。
(该代码仅提供了一种基本的限制方式,并且仅在单个应用程序实例中生效。如果需要更高级的限制功能,例如跨多个应用程序实例或服务器的限制,则可能需要使用其他技术,例如分布式缓存或负载均衡器)
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import java.util.concurrent.TimeUnit;
/**
* @Author lyuf
* @Date 2023/4/18
* @Version 1.0
* @Describe 用于限制用户调用接口次数
*/
public class ApiLimiter {
private static Cache<String, Integer> cache;
public ApiLimiter() {
this.cache = CacheBuilder.newBuilder()
.maximumSize(1000) // 最多缓存1000个记录
.expireAfterWrite(10, TimeUnit.MINUTES) // 10分钟后过期
.build();
}
public static boolean isLimitExceeded(String userId, int maxRequests) {
int count = 0;
Integer cachedCount = cache.getIfPresent(userId);
if (cachedCount != null) {
count = cachedCount;
}
if (count >= maxRequests) {
return true;
} else {
count++;
cache.put(userId, count);
return false;
}
}
}
自定义AOP:
/**
* @Author lyuf
* @Date 2023/4/14
* @Version 1.0
* @Describe 用于拦截请求AOP
*/
@Component
@Aspect
@Slf4j
public class MyInterceptor {
@Lazy
@Resource
private CommonAPI commonApi;
@Autowired
private ISysUserRoleService sysUserRoleService;
@Pointcut("@annotation(org.jeecg.modules.aop.AspectAnnotation)")
private void annotationPointCut() {
}
@Before("annotationPointCut()")
public void checkToken(JoinPoint joinPoint) {
// 获取方法
Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();
// 获取AspectAnnotation注解
AspectAnnotation aspectAnnotation = method.getAnnotation(AspectAnnotation.class);
// 校验用户调用接口次数
if (aspectAnnotation.checkCount()) {
// 获取用户角色Id
SysUser user = getUserRoleInfo();
String userId = user .getUserId();
if (StringUtils.isBlank(userId)) {
log.info("校验未通过...");
throw new AuthenticationException("用户id获取为空!");
}
boolean limitExceeded = ApiLimiter.isLimitExceeded(userId, 10);
if (limitExceeded) {
log.info("校验未通过...");
throw new AuthenticationException("用户调用频率过高,请十分钟后重试!");
} else {
log.info("接口次数校验通过");
}
}
}
}
自定义注解:
/**
* @Author lyuf
* @Date 2023/4/14 10:39
* @Version 1.0
*/
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface AspectAnnotation {
boolean checkCount() default false;
}
注解使用:
/**
* test
*
* @param params
* @return 数据
*/
@PutMapping("/admin/update")
@AspectAnnotation(checkCount= true)
public String updateAdmin(@RequestBody String params) {
// 业务代码
return "";
}