//java
import com.XXX.base.annotation.safety.PostTimesLimit;redis
import java.lang.annotation.ElementType;spring
import java.lang.annotation.Retention;apache
import java.lang.annotation.RetentionPolicy;app
import java.lang.annotation.Target;post
@Retention(RetentionPolicy.RUNTIME)url
@Target({ElementType.METHOD})spa
public @interface PostTimesLimitContainer {.net
PostTimesLimit[] value();code
}
//
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.core.annotation.Order;
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
@Documented
@Order(-2147483648)
public @interface PostTimesLimit {
int count() default 2147483647;
long time() default 60000L;
String key();
}
//
import com.XXX.base.annotation.safety.PostTimesLimit;
import com.XXX.base.annotation.safety.PostTimesLimitContainer;
import org.apache.commons.lang3.ArrayUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import java.util.concurrent.TimeUnit;
@Aspect
@Component
public class PostTimesLimitAspect {
private final static org.slf4j.Logger logger = LoggerFactory.getLogger(PostTimesLimitAspect.class);
@Autowired
private RedisTemplate redisTemplate;
private void limit(PostTimesLimit limit, String ip, String url) {
String key = limit.key().concat(url).concat(ip);
ValueOperations operations= redisTemplate.opsForValue();
//計數器加一
long count = operations.increment(key, 1);
if (count == 1) {
redisTemplate.expire(key, limit.time(), TimeUnit.MILLISECONDS);
}
if (count > limit.count()) {
String msg = String.format("IP[%s]在規定的時間內[%d]ms,訪問地址[%s]超過了限定的次數[%d]",ip,limit.time(),url,limit.count());
logger.info(msg);
throw new PostTimesLimitException(msg);
}
}
// 切面
//@before 通知
//"@annotation(limit)" 切入點
@Before("@annotation(limit)")
public void postTimesLimit(final JoinPoint joinPoint,PostTimesLimit limit) {
try {
Object[] args = joinPoint.getArgs();
if(ArrayUtils.isEmpty(args)){
throw new PostTimesLimitException("方法中缺失參數");
}
HttpServletRequest request = null;
for (int i = 0; i < args.length; i++) {
if (args[i] instanceof HttpServletRequest) {
request = (HttpServletRequest) args[i];
break;
}
}
if (request == null) {
throw new PostTimesLimitException("方法中缺失HttpServletRequest參數");
}
String ip = getClientIp(request);
String url = request.getServletPath().toString();
limit(limit, ip, url);
} catch (PostTimesLimitException e) {
throw e;
} catch (Exception e) {
logger.error("訪問限制發送錯誤: ", e);
}
}
@Before("@annotation(limits)")
public void postTimesLimits(final JoinPoint joinPoint,PostTimesLimitContainer limits) throws PostTimesLimitException {
try {
Object[] args = joinPoint.getArgs();
if(ArrayUtils.isEmpty(args)){
throw new PostTimesLimitException("方法中缺失參數");
}
if(limits==null||limits.value()==null){
throw new PostTimesLimitException("annotation中缺失參數");
}
HttpServletRequest request = null;
for (int i = 0; i < args.length; i++) {
if (args[i] instanceof HttpServletRequest) {
request = (HttpServletRequest) args[i];
break;
}
}
if (request == null) {
throw new PostTimesLimitException("方法中缺失HttpServletRequest參數");
}
String ip = getClientIp(request);
String url = request.getServletPath().toString();
for (PostTimesLimit limit : limits.value()) {
limit(limit, ip, url);
}
} catch (PostTimesLimitException e) {
throw e;
} catch (Exception e) {
logger.error("訪問限制發送錯誤: ", e);
}
}
}
//
@PostTimesLimitContainer({
@PostTimesLimit(key="SMS_60S_Rule",count=1,time=60000),
@PostTimesLimit(key="SMS_5M_Rule",count=3,time=300000),
@PostTimesLimit(key="SMS_1H_Rule",count=30,time=3600000)})
@RequestMapping(value = "/mobile/sendcode", method = RequestMethod.GET)
<!-- Enable AspectJ style of Spring AOP -->
<aop:aspectj-autoproxy proxy-target-class="true"/>