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