前言
今天項目需要對接口進行限制訪問次數(某個時間段只允許訪問幾次) 項目經理說是做成一個AOP 小組件的形式,便於以後其他項目複用,這次我們是用Redis+AOP 實現的。
實現邏輯:
使用請求頭中的userid 來作爲key 登陸次數作爲value
請求時,判斷Redis中是否有key ,if(沒有) {新增key+value+定時時間}
if(有){判斷value是否超出規定次數 if(超出){異常返回} if(未超出){ value+1 更新數據}}
1.自定義註解類
/**
* @Description: 接口限流自定義註解
* @author: xuxinku
* @Date: 2020/4/6 14:18
* @ModifiedDate:
* @Copyright:XX保險股份有限公司
*/
@Documented
@Target(ElementType.METHOD) //代表方法級別
@Inherited //是指定某個自定義註解如果寫在了父類的聲明部分,那麼子類的聲明部分也能自動擁有該註解
@Retention(RetentionPolicy.RUNTIME)
public @interface AccessRestriction {
/**
* 多長時間內限制,默認 60
* @return
*/
long t () default 60;
/**
* 單位時間內能訪問多少次,默認3次
* @return
*/
int count () default 3;
}
2.切面類實現
/**
* @Description: 接口限流使用
* @author: xuxinku
* @Date: 2020/4/6 14:18
* @ModifiedDate:
* @Copyright:江泰保險股份有限公司
*/
@Aspect
@Component
@Slf4j
public class AccessRestrictionAop {
@Autowired
private RedisTemplate<String, String> redisTemplate;
@Pointcut("@annotation(com.jtpf.es.aop.AccessRestriction)")
public void executeService() {
}
@Before("executeService()&&@annotation(accessRestriction)")
public void checkIP(JoinPoint joinPoint, AccessRestriction accessRestriction)throws Exception {
// 規則:不存在 賦初始值/存在且大於 拋出超時異常/ 次數+1
String key = LoginAop.getUserCode();
//根據key 查詢是否存在 hasKey
String oldKay = redisTemplate.opsForValue().get(key);
// 不存在 賦初始值
if(StringUtils.isEmpty(oldKay)){
redisTemplate.opsForValue().set(key, "0", accessRestriction.t(), TimeUnit.SECONDS );
}
//存在且大於規定次數
int frequency = Integer.valueOf(redisTemplate.opsForValue().get(key)).intValue();
if (!StringUtils.isEmpty(oldKay) && frequency > accessRestriction.count()){
throw new BizException("當前用戶已請求次數過多 請稍後重試");
}
//次數+1
int i = frequency +1;
//更新記錄次數
redisTemplate.opsForValue().set(key, i+"",0 );
log.info("開始記錄操作信息 ");
System.out.println("請求次數=====》"+oldKay);
System.out.println("key=====》"+key);
log.info("記錄操作信息成功");
}
3.LoginAop工具
/**
* 必須添加這兩個註解:Component: 泛指組件,講該類納入bean中;
* Aspect: 通過該註解及表達式就可以輕鬆的使用POJO來定義切面
* * @author tdh
*/
@Aspect
@Slf4j
@Component
public class LoginAop {
/**
* 獲取用戶編碼
*
* @return
*/
public static String getUserCode() {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
String userCode = request.getHeader("jtpf.userId");
if (userCode == null) {
userCode = request.getParameter("jtpf.userId");
}
if (StringUtils.isBlank(userCode)) {
throw new ParamException("請重新登陸");
} else {
return userCode;
}
}
/**
* 獲取分支機構編碼
*
* @return
*/
public static String getBranchCode() {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
if (StringUtils.isNotBlank(request.getHeader("branchCode"))) {
return URLDecoder.decode(request.getHeader("branchCode"));
} else {
return null;
}
}
/**
* 獲取總公司機構編碼
*
* @return
*/
public static String getParentCode() {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
if (StringUtils.isNotBlank(request.getHeader("parentCode"))) {
return URLDecoder.decode(request.getHeader("parentCode"));
} else {
return null;
}
}
/**
* 獲取用戶名稱
*
* @return
*/
public static String getUserName() {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
if (StringUtils.isNotBlank(request.getHeader("userName"))) {
return URLDecoder.decode(request.getHeader("userName"));
} else {
return null;
}
}
/**
* 定義攔截規則:攔截標有com.SheetChrist.annotation.Login類中註解的所有方法com.flow.controller.FlowController
*/
@Pointcut("execution(public * com.*.controller.*.*(..))")
public void loginMethodPointcut() {
}
@Around("loginMethodPointcut()")
public Object interceptor(ProceedingJoinPoint point) throws Throwable {
//返回的結果
Object result = null;
result = point.proceed();
return result;
}
public static String getIpAddr(){
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
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();
}
if ("0:0:0:0:0:0:0:1".equals(ip)) {
ip = "127.0.0.1";
}
if (ip.split(",").length > 1) {
ip = ip.split(",")[0];
}
return ip;
}
}
4.註解的使用
60秒 5次
@AccessRestriction(t =60, count = 5)
@GetMapping(value = "/testAOP2")
public void testAop2() {
System.out.println("限流測試===========結束》");
}
測試:
每天記錄發現的問題,不進步就是在退步