10 changed files with 457 additions and 18 deletions
@ -0,0 +1,63 @@ |
|||||
|
package com.bnyer.order.annotation; |
||||
|
|
||||
|
import java.lang.annotation.Documented; |
||||
|
import java.lang.annotation.Retention; |
||||
|
import java.lang.annotation.RetentionPolicy; |
||||
|
import java.lang.annotation.Target; |
||||
|
|
||||
|
import static java.lang.annotation.ElementType.METHOD; |
||||
|
|
||||
|
/** |
||||
|
* @author :WXC |
||||
|
* @Date :2023/05/12 |
||||
|
* @description : 防重复提交 |
||||
|
*/ |
||||
|
@Target({ METHOD}) |
||||
|
@Retention(RetentionPolicy.RUNTIME) |
||||
|
@Documented |
||||
|
public @interface LimitRepeatRequest { |
||||
|
|
||||
|
/** |
||||
|
* 限制当前用户同一个api 不能重复提交 |
||||
|
*/ |
||||
|
String SELF="self"; |
||||
|
|
||||
|
/** |
||||
|
* 限制所有用户同一个参数不能重复提交,如我新增了用户 张三, 那么其他人不能再并发情况下重复添加张三 |
||||
|
*/ |
||||
|
String ALL_USER="all"; |
||||
|
|
||||
|
/** |
||||
|
* 当前时间内 api 只能请求一次,单位秒 |
||||
|
* @return |
||||
|
*/ |
||||
|
int time() default 5; |
||||
|
|
||||
|
/** |
||||
|
* 对部分参数做重复请求限制 |
||||
|
* @return |
||||
|
*/ |
||||
|
String[] bodyParam() default {}; |
||||
|
|
||||
|
/** |
||||
|
* 是否对全部参数做重复请求限制 |
||||
|
* @return |
||||
|
*/ |
||||
|
boolean bodyAllParam() default false; |
||||
|
|
||||
|
/** |
||||
|
* 重复请求限制的用户范围 |
||||
|
* LimitRepeatRequest.SELF:针对当前登录用户 |
||||
|
* LimitRepeatRequest.ALL_USER:针对所有用户 |
||||
|
* |
||||
|
* @return |
||||
|
*/ |
||||
|
String userRange() default SELF; |
||||
|
|
||||
|
/** |
||||
|
* 错误提示信息 |
||||
|
* @return |
||||
|
*/ |
||||
|
String message() default ""; |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,150 @@ |
|||||
|
package com.bnyer.order.aop; |
||||
|
|
||||
|
import cn.hutool.core.collection.CollUtil; |
||||
|
import com.alibaba.fastjson.JSON; |
||||
|
import com.alibaba.fastjson.JSONArray; |
||||
|
import com.alibaba.fastjson.JSONObject; |
||||
|
import com.bnyer.common.core.domain.R; |
||||
|
import com.bnyer.common.core.utils.MD5Util; |
||||
|
import com.bnyer.common.core.utils.StringUtils; |
||||
|
import com.bnyer.common.core.vo.UserInfoVo; |
||||
|
import com.bnyer.common.redis.service.RedisService; |
||||
|
import com.bnyer.common.security.utils.SecurityUtils; |
||||
|
import com.bnyer.order.annotation.LimitRepeatRequest; |
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
import org.aspectj.lang.ProceedingJoinPoint; |
||||
|
import org.aspectj.lang.Signature; |
||||
|
import org.aspectj.lang.annotation.Around; |
||||
|
import org.aspectj.lang.annotation.Aspect; |
||||
|
import org.aspectj.lang.reflect.MethodSignature; |
||||
|
import org.springframework.beans.factory.annotation.Autowired; |
||||
|
import org.springframework.stereotype.Component; |
||||
|
|
||||
|
import java.lang.reflect.Method; |
||||
|
import java.util.Objects; |
||||
|
import java.util.concurrent.TimeUnit; |
||||
|
|
||||
|
/** |
||||
|
* @author :WXC |
||||
|
* @Date :2023/05/12 |
||||
|
* @description :重复提交限制aop |
||||
|
*/ |
||||
|
@Slf4j |
||||
|
@Aspect |
||||
|
@Component |
||||
|
public class LimitRepeatRequestAspect { |
||||
|
|
||||
|
@Autowired |
||||
|
private RedisService redisService; |
||||
|
|
||||
|
@Around("@annotation(limitRepeatRequest)") |
||||
|
public Object around(ProceedingJoinPoint joinPoint, LimitRepeatRequest limitRepeatRequest) throws Throwable{ |
||||
|
String key = getKey(joinPoint,limitRepeatRequest); |
||||
|
Object cacheObject = redisService.getCacheObject(key); |
||||
|
Object message; |
||||
|
if (Objects.nonNull(cacheObject)){ |
||||
|
if (StringUtils.isNotBlank(limitRepeatRequest.message())){ |
||||
|
message = limitRepeatRequest.message(); |
||||
|
}else { |
||||
|
message = new R().buildRepeatRequest(limitRepeatRequest.time()); |
||||
|
} |
||||
|
}else { |
||||
|
redisService.setCacheObject(key,"1",(long)limitRepeatRequest.time(), TimeUnit.SECONDS); |
||||
|
message = joinPoint.proceed(); |
||||
|
} |
||||
|
return message; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取rediskey |
||||
|
* |
||||
|
* @param joinPoint |
||||
|
* @param limitRepeatRequest |
||||
|
* @return |
||||
|
*/ |
||||
|
private String getKey(ProceedingJoinPoint joinPoint, LimitRepeatRequest limitRepeatRequest) { |
||||
|
UserInfoVo userInfo = SecurityUtils.getUserInfo(); |
||||
|
Method currentMethod = getCurrentMethod(joinPoint); |
||||
|
//最后拼接好的key
|
||||
|
StringBuilder key = new StringBuilder("LimitRepeatRequestAspect#" + currentMethod.getName()); |
||||
|
//限制范围
|
||||
|
String userRange = limitRepeatRequest.userRange(); |
||||
|
if (LimitRepeatRequest.SELF.equals(userRange)){ |
||||
|
key.append("#"); |
||||
|
key.append(userInfo.getUserClientType()); |
||||
|
key.append("#"); |
||||
|
key.append(userInfo.getId()); |
||||
|
} |
||||
|
//获取请求参数
|
||||
|
JSONObject requestParams = getRequestParams(joinPoint); |
||||
|
//部分参数做重复请求限制
|
||||
|
if (Objects.nonNull(requestParams)){ |
||||
|
String[] bodyParam = limitRepeatRequest.bodyParam(); |
||||
|
if (bodyParam != null){ |
||||
|
for (String param : bodyParam) { |
||||
|
key.append("#"); |
||||
|
Object obj = requestParams.get(param); |
||||
|
if (obj instanceof JSONArray){ |
||||
|
JSONArray jsonArray = requestParams.getJSONArray(param); |
||||
|
if (CollUtil.isNotEmpty(jsonArray)){ |
||||
|
for (Object o : jsonArray) { |
||||
|
if (o instanceof String){ |
||||
|
key.append(o); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
}else if(obj instanceof String){ |
||||
|
String value = requestParams.containsKey(param)?requestParams.getString(param):""; |
||||
|
key.append(value); |
||||
|
}else if(obj instanceof Integer){ |
||||
|
String value = requestParams.containsKey(param)?requestParams.getString(param):""; |
||||
|
key.append(value); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
//全部参数做重复请求限制
|
||||
|
if (limitRepeatRequest.bodyAllParam()){ |
||||
|
String jsonStr = JSON.toJSONString(requestParams); |
||||
|
key.append("#"); |
||||
|
key.append(jsonStr); |
||||
|
} |
||||
|
return MD5Util.getMD5String(key.toString()); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取请求参数 |
||||
|
* @param joinPoint |
||||
|
* @return |
||||
|
*/ |
||||
|
private JSONObject getRequestParams(ProceedingJoinPoint joinPoint) { |
||||
|
Object[] args = joinPoint.getArgs(); |
||||
|
if (args != null && args.length != 0){ |
||||
|
String jsonString = JSON.toJSONString(args); |
||||
|
JSONArray parseArray = JSON.parseArray(jsonString); |
||||
|
return parseArray.getJSONObject(0); |
||||
|
} |
||||
|
return null; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取当前方法 |
||||
|
* @param joinPoint |
||||
|
* @return |
||||
|
*/ |
||||
|
private Method getCurrentMethod(ProceedingJoinPoint joinPoint) { |
||||
|
Signature signature = joinPoint.getSignature(); |
||||
|
if (!(signature instanceof MethodSignature)){ |
||||
|
throw new IllegalArgumentException("该注解只能作用于方法上面"); |
||||
|
} |
||||
|
MethodSignature methodSignature = (MethodSignature)signature; |
||||
|
Object target = joinPoint.getTarget(); |
||||
|
try { |
||||
|
Method method = target.getClass().getMethod(methodSignature.getName(), methodSignature.getParameterTypes()); |
||||
|
return method; |
||||
|
} catch (NoSuchMethodException e) { |
||||
|
throw new RuntimeException(e); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,63 @@ |
|||||
|
package com.bnyer.pay.annotation; |
||||
|
|
||||
|
import java.lang.annotation.Documented; |
||||
|
import java.lang.annotation.Retention; |
||||
|
import java.lang.annotation.RetentionPolicy; |
||||
|
import java.lang.annotation.Target; |
||||
|
|
||||
|
import static java.lang.annotation.ElementType.METHOD; |
||||
|
|
||||
|
/** |
||||
|
* @author :WXC |
||||
|
* @Date :2023/05/12 |
||||
|
* @description : 防重复提交 |
||||
|
*/ |
||||
|
@Target({ METHOD}) |
||||
|
@Retention(RetentionPolicy.RUNTIME) |
||||
|
@Documented |
||||
|
public @interface LimitRepeatRequest { |
||||
|
|
||||
|
/** |
||||
|
* 限制当前用户同一个api 不能重复提交 |
||||
|
*/ |
||||
|
String SELF="self"; |
||||
|
|
||||
|
/** |
||||
|
* 限制所有用户同一个参数不能重复提交,如我新增了用户 张三, 那么其他人不能再并发情况下重复添加张三 |
||||
|
*/ |
||||
|
String ALL_USER="all"; |
||||
|
|
||||
|
/** |
||||
|
* 当前时间内 api 只能请求一次,单位秒 |
||||
|
* @return |
||||
|
*/ |
||||
|
int time() default 5; |
||||
|
|
||||
|
/** |
||||
|
* 对部分参数做重复请求限制 |
||||
|
* @return |
||||
|
*/ |
||||
|
String[] bodyParam() default {}; |
||||
|
|
||||
|
/** |
||||
|
* 是否对全部参数做重复请求限制 |
||||
|
* @return |
||||
|
*/ |
||||
|
boolean bodyAllParam() default false; |
||||
|
|
||||
|
/** |
||||
|
* 重复请求限制的用户范围 |
||||
|
* LimitRepeatRequest.SELF:针对当前登录用户 |
||||
|
* LimitRepeatRequest.ALL_USER:针对所有用户 |
||||
|
* |
||||
|
* @return |
||||
|
*/ |
||||
|
String userRange() default SELF; |
||||
|
|
||||
|
/** |
||||
|
* 错误提示信息 |
||||
|
* @return |
||||
|
*/ |
||||
|
String message() default ""; |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,150 @@ |
|||||
|
package com.bnyer.pay.aop; |
||||
|
|
||||
|
import cn.hutool.core.collection.CollUtil; |
||||
|
import com.alibaba.fastjson.JSON; |
||||
|
import com.alibaba.fastjson.JSONArray; |
||||
|
import com.alibaba.fastjson.JSONObject; |
||||
|
import com.bnyer.common.core.domain.R; |
||||
|
import com.bnyer.common.core.utils.MD5Util; |
||||
|
import com.bnyer.common.core.utils.StringUtils; |
||||
|
import com.bnyer.common.core.vo.UserInfoVo; |
||||
|
import com.bnyer.common.redis.service.RedisService; |
||||
|
import com.bnyer.common.security.utils.SecurityUtils; |
||||
|
import com.bnyer.pay.annotation.LimitRepeatRequest; |
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
import org.aspectj.lang.ProceedingJoinPoint; |
||||
|
import org.aspectj.lang.Signature; |
||||
|
import org.aspectj.lang.annotation.Around; |
||||
|
import org.aspectj.lang.annotation.Aspect; |
||||
|
import org.aspectj.lang.reflect.MethodSignature; |
||||
|
import org.springframework.beans.factory.annotation.Autowired; |
||||
|
import org.springframework.stereotype.Component; |
||||
|
|
||||
|
import java.lang.reflect.Method; |
||||
|
import java.util.Objects; |
||||
|
import java.util.concurrent.TimeUnit; |
||||
|
|
||||
|
/** |
||||
|
* @author :WXC |
||||
|
* @Date :2023/05/12 |
||||
|
* @description :重复提交限制aop |
||||
|
*/ |
||||
|
@Slf4j |
||||
|
@Aspect |
||||
|
@Component |
||||
|
public class LimitRepeatRequestAspect { |
||||
|
|
||||
|
@Autowired |
||||
|
private RedisService redisService; |
||||
|
|
||||
|
@Around("@annotation(limitRepeatRequest)") |
||||
|
public Object around(ProceedingJoinPoint joinPoint, LimitRepeatRequest limitRepeatRequest) throws Throwable{ |
||||
|
String key = getKey(joinPoint,limitRepeatRequest); |
||||
|
Object cacheObject = redisService.getCacheObject(key); |
||||
|
Object message; |
||||
|
if (Objects.nonNull(cacheObject)){ |
||||
|
if (StringUtils.isNotBlank(limitRepeatRequest.message())){ |
||||
|
message = limitRepeatRequest.message(); |
||||
|
}else { |
||||
|
message = new R().buildRepeatRequest(limitRepeatRequest.time()); |
||||
|
} |
||||
|
}else { |
||||
|
redisService.setCacheObject(key,"1",(long)limitRepeatRequest.time(), TimeUnit.SECONDS); |
||||
|
message = joinPoint.proceed(); |
||||
|
} |
||||
|
return message; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取rediskey |
||||
|
* |
||||
|
* @param joinPoint |
||||
|
* @param limitRepeatRequest |
||||
|
* @return |
||||
|
*/ |
||||
|
private String getKey(ProceedingJoinPoint joinPoint, LimitRepeatRequest limitRepeatRequest) { |
||||
|
UserInfoVo userInfo = SecurityUtils.getUserInfo(); |
||||
|
Method currentMethod = getCurrentMethod(joinPoint); |
||||
|
//最后拼接好的key
|
||||
|
StringBuilder key = new StringBuilder("LimitRepeatRequestAspect#" + currentMethod.getName()); |
||||
|
//限制范围
|
||||
|
String userRange = limitRepeatRequest.userRange(); |
||||
|
if (LimitRepeatRequest.SELF.equals(userRange)){ |
||||
|
key.append("#"); |
||||
|
key.append(userInfo.getUserClientType()); |
||||
|
key.append("#"); |
||||
|
key.append(userInfo.getId()); |
||||
|
} |
||||
|
//获取请求参数
|
||||
|
JSONObject requestParams = getRequestParams(joinPoint); |
||||
|
//部分参数做重复请求限制
|
||||
|
if (Objects.nonNull(requestParams)){ |
||||
|
String[] bodyParam = limitRepeatRequest.bodyParam(); |
||||
|
if (bodyParam != null){ |
||||
|
for (String param : bodyParam) { |
||||
|
key.append("#"); |
||||
|
Object obj = requestParams.get(param); |
||||
|
if (obj instanceof JSONArray){ |
||||
|
JSONArray jsonArray = requestParams.getJSONArray(param); |
||||
|
if (CollUtil.isNotEmpty(jsonArray)){ |
||||
|
for (Object o : jsonArray) { |
||||
|
if (o instanceof String){ |
||||
|
key.append(o); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
}else if(obj instanceof String){ |
||||
|
String value = requestParams.containsKey(param)?requestParams.getString(param):""; |
||||
|
key.append(value); |
||||
|
}else if(obj instanceof Integer){ |
||||
|
String value = requestParams.containsKey(param)?requestParams.getString(param):""; |
||||
|
key.append(value); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
//全部参数做重复请求限制
|
||||
|
if (limitRepeatRequest.bodyAllParam()){ |
||||
|
String jsonStr = JSON.toJSONString(requestParams); |
||||
|
key.append("#"); |
||||
|
key.append(jsonStr); |
||||
|
} |
||||
|
return MD5Util.getMD5String(key.toString()); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取请求参数 |
||||
|
* @param joinPoint |
||||
|
* @return |
||||
|
*/ |
||||
|
private JSONObject getRequestParams(ProceedingJoinPoint joinPoint) { |
||||
|
Object[] args = joinPoint.getArgs(); |
||||
|
if (args != null && args.length != 0){ |
||||
|
String jsonString = JSON.toJSONString(args); |
||||
|
JSONArray parseArray = JSON.parseArray(jsonString); |
||||
|
return parseArray.getJSONObject(0); |
||||
|
} |
||||
|
return null; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取当前方法 |
||||
|
* @param joinPoint |
||||
|
* @return |
||||
|
*/ |
||||
|
private Method getCurrentMethod(ProceedingJoinPoint joinPoint) { |
||||
|
Signature signature = joinPoint.getSignature(); |
||||
|
if (!(signature instanceof MethodSignature)){ |
||||
|
throw new IllegalArgumentException("该注解只能作用于方法上面"); |
||||
|
} |
||||
|
MethodSignature methodSignature = (MethodSignature)signature; |
||||
|
Object target = joinPoint.getTarget(); |
||||
|
try { |
||||
|
Method method = target.getClass().getMethod(methodSignature.getName(), methodSignature.getParameterTypes()); |
||||
|
return method; |
||||
|
} catch (NoSuchMethodException e) { |
||||
|
throw new RuntimeException(e); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
} |
||||
Loading…
Reference in new issue