AOP+自定义注解+Redis 控制ip(或token里的用户)访问接口次数
一、自定义注解
以下为10s以内访问10次,实际中按需求更改默认值或方法上修改为实际值
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface RequestLimit {
int limit ()default 10;
int time() default 10;
}
二、测试的controller
@RestController
@Slf4j
public class TestController {
@RequestLimit(time = 3,limit = 3)
@GetMapping(value = "/hello",produces="application/json;charset=UTF-8")
public String hello() {
Map m=new HashMap<>();
m.put("帝国骑士","李信");
String result=Response.success(m);
return result;
}
}
三、切面处理类
import javax.servlet.http.HttpServletRequest;
@Aspect
@Component
@Slf4j
public class RequestLimitAspect {
@Autowired
HttpServletRequest request;
@Autowired
RedisService redisService;
/**
* 被此注解标明的方法会被代理
*/
@Pointcut("@annotation(com.master.zhi.annotation.RequestLimit)")
public void limitPointCut() {
}
/**
*环绕通知拿到方法上RequestLimit注解的2个属性值 以作比较
*/
@Around("limitPointCut()")
public Object around(ProceedingJoinPoint point) throws Throwable {
MethodSignature signature = (MethodSignature) point.getSignature();
RequestLimit requestLimit = signature.getMethod().getAnnotation(RequestLimit.class);
if (requestLimit != null) {
String key = getCacheKey();
int limit = requestLimit.limit();
int time = requestLimit.time();
cacheOrRefuse(key, limit, time);
}
Object result = point.proceed();
return result;
}
/**
* ip+url作为资源的key
*/
private String getCacheKey() {
String key = getIpAddress(request) + ":" + request.getRequestURI();
return key;
}
/**
*达到次数 停止服务 Or 访问次数+1
*/
private void cacheOrRefuse(String key, int limit, int time) {
if (redisService.hasKey(key)) {
String count = redisService.getObject(key).toString();
int countRequest = Integer.parseInt(count);
if (countRequest > limit) {
throw new BusinessException("Bad request", "MAS-00-50001", HttpStatus.BAD_REQUEST);
} else {
redisService.increment(key, 1);
}
} else {
redisService.setForTime(key, 1, time);
}
}
/**
* 获取实际访问者ip
* 注意 通过ha或者nginx等代理的ip除了最后一次的访问ip 如(ip1,ip2,ip3,)
* 有可能篡改ip1或p2,只有ip3无法篡改,这时候只能判断ip3,
* 所以可以根据实际业务可以拿用户信息(比如token里面获取的用户信息)作为访问记录
*/
public static String getIpAddress(HttpServletRequest request) {
String ip = request.getHeader("x-forwarded-for");
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("WL-Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
}
return ip;
}
}
四、实际效果
1.正常访问未达到限制
2.达到次数限制