接口限制

This commit is contained in:
wangw 2024-12-17 11:25:06 +08:00
parent 42b33c5f0e
commit 85f71935cd
4 changed files with 171 additions and 9 deletions

View File

@ -1,7 +1,6 @@
package com.sqx.common.aspect;
import com.google.gson.Gson;
import com.sqx.common.utils.DateUtils;
import com.sqx.common.utils.HttpContextUtils;
import com.sqx.common.utils.IPUtils;
import lombok.extern.slf4j.Slf4j;
@ -10,11 +9,9 @@ import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import java.util.Date;
/**
* 方法调用统一切面处理
@ -23,12 +20,10 @@ import java.util.Date;
@Component
@Slf4j
public class AppApiMethodAspect {
@Pointcut("execution(public * (" +
"com.sqx.modules.*.controller.* " +
").*(..)) && " +
"!execution(public * (" +
"com.sqx.modules.*.controller.SysLoginController " +
").*(..))")
@Pointcut("execution(public * (com.sqx.modules.*.controller.*).*(..)) " +
"&& " +
"!execution(public * (com.sqx.modules.sys.controller.SysLoginController).*(..))")
public void pkg() {
}

View File

@ -0,0 +1,147 @@
package com.sqx.common.utils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.time.DayOfWeek;
import java.time.LocalDate;
import java.time.ZoneOffset;
import java.time.temporal.TemporalAdjusters;
import java.util.Objects;
/**
* 接口访问次数 月为单位
*/
@Component
public class ApiAccessLimitUtil {
private static final String ACCESS_COUNT_KEY_PREFIX = "sys:limit:";
private static RedisUtils redisUtils;
private static final int DEFAULT_ACCESS_COUNT = 5;
private static final String DATE_TIME_FORMAT = "month";
@Autowired
public void setRedisUtils(RedisUtils redisUtils) {
ApiAccessLimitUtil.redisUtils = redisUtils;
}
/**
* 默认 当月5次
* @param id 唯一值
* @param key 接口名称 sys:limit:接口名称
* @return
*/
public static boolean isAccessAllowed(String id, String key) {
String redisKey = generateRedisKey(key, id);
Object countObj = redisUtils.get(redisKey);
if (countObj == null) {
// 根据不同时间周期设置过期时间并初始化访问次数为1
long expireAt = calculateExpireAt(DATE_TIME_FORMAT);
redisUtils.set(redisKey, 1, expireAt);
return true;
}
if ((int) countObj < DEFAULT_ACCESS_COUNT) {
// 访问次数未达上限次数加1
redisUtils.incr(redisKey);
return true;
}
return false;
}
/**
* 默认月 month//自然月
* @param id 唯一值
* @param key 接口名称 sys:limit:接口名称
* @param count 次数限制
* @return
*/
public static boolean isAccessAllowed(String id, String key, Integer count) {
String redisKey = generateRedisKey(key, id);
Object countObj = redisUtils.get(redisKey);
if (countObj == null) {
// 根据不同时间周期设置过期时间并初始化访问次数为1
long expireAt = calculateExpireAt(DATE_TIME_FORMAT);
redisUtils.set(redisKey, 1, expireAt);
return true;
}
if ((int) countObj < count) {
// 访问次数未达上限次数加1
redisUtils.incr(redisKey);
return true;
}
return false;
}
/**
* 默认 5次
* @param id 唯一值
* @param key 接口名称 sys:limit:接口名称
* @param timeFormat day//自然天 week//本周日 month//自然月 year//自然年
* @return
*/
public static boolean isAccessAllowed(String id, String key, String timeFormat) {
String redisKey = generateRedisKey(key, id);
Object countObj = redisUtils.get(redisKey);
if (countObj == null) {
// 根据不同时间周期设置过期时间并初始化访问次数为1
long expireAt = calculateExpireAt(timeFormat);
redisUtils.set(redisKey, 1, expireAt);
return true;
}
if ((int) countObj < DEFAULT_ACCESS_COUNT) {
// 访问次数未达上限次数加1
redisUtils.incr(redisKey);
return true;
}
return false;
}
/**
* @param id 唯一值
* @param key 接口名称 sys:limit:接口名称
* @param count 次数限制
* @param timeFormat day//自然天 week//本周日 month//自然月 year//自然年
* @return
*/
public static boolean isAccessAllowed(String id, String key, Integer count, String timeFormat) {
String redisKey = generateRedisKey(key, id);
Object countObj = redisUtils.get(redisKey);
if (countObj == null) {
// 根据不同时间周期设置过期时间并初始化访问次数为1
long expireAt = calculateExpireAt(timeFormat);
redisUtils.set(redisKey, 1, expireAt);
return true;
}
if ((int) countObj < count) {
// 访问次数未达上限次数加1
redisUtils.incr(redisKey);
return true;
}
return false;
}
private static String generateRedisKey(String key, String id) {
return ACCESS_COUNT_KEY_PREFIX + key + ":" + id;
}
private static long calculateExpireAt(String timePeriod) {
LocalDate now = LocalDate.now();
LocalDate expireDate = null;
if ("day".equals(timePeriod)) {
expireDate = now.plusDays(1).atStartOfDay().toLocalDate();
} else if ("week".equals(timePeriod)) {
expireDate = now.plusWeeks(0).with(TemporalAdjusters.nextOrSame(java.time.DayOfWeek.SUNDAY));
} else if ("month".equals(timePeriod)) {
expireDate = now.plusMonths(1).withDayOfMonth(1).minusDays(1);
} else if ("year".equals(timePeriod)) {
expireDate = now.plusYears(1).withDayOfYear(1).minusDays(1);
}
return Objects.requireNonNull(expireDate).atTime(23, 59, 59).toEpochSecond(ZoneOffset.UTC);
}
}

View File

@ -128,6 +128,21 @@ public class RedisUtils {
return get(key, NOT_EXPIRE);
}
/**
* 对指定的键执行自增操作返回自增后的值
*
* @param key 要自增的键
* @return 自增后的值如果键不存在则初始化为1后返回1如果出现异常返回null
*/
public Long incr(String key) {
try {
return redisTemplate.opsForValue().increment(key);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
public void delete(String key) {
redisTemplate.delete(key);
}

View File

@ -2,6 +2,8 @@ package com.sqx.modules.app.controller.app;
import com.alibaba.fastjson.JSONObject;
import com.sqx.common.annotation.Debounce;
import com.sqx.common.utils.ApiAccessLimitUtil;
import com.sqx.common.utils.Result;
import com.sqx.modules.app.annotation.Login;
import com.sqx.modules.app.annotation.LoginUser;
@ -72,6 +74,9 @@ public class AppController {
@ApiOperation("用户修改个人信息")
@ResponseBody
public Result updateUserImageUrl(@RequestAttribute("userId") Long userId,String zhiFuBao,String zhiFuBaoName) {
if(!ApiAccessLimitUtil.isAccessAllowed(userId.toString(), "updateZFB", 2, "month")){
return Result.error("每月仅支持修改两次,请联系管理员");
}
UserEntity userEntity=new UserEntity();
userEntity.setZhiFuBao(zhiFuBao);
userEntity.setZhiFuBaoName(zhiFuBaoName);