目录
1.创建@Limit注解
2.自定义切面
3.解决依赖问题
(1).HttpStatus.MAXLIMIT
(2).NetworkUtils
(3).ResponseUtil
4.使用
5.运行错误可能的原因
1.创建@Limit注解
package com.mimiwang.common.annotation;import java.lang.annotation.*;import java.util.concurrent.TimeUnit;@Target({ElementType.METHOD,ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)@Documentedpublic @interface Limit {// redisKey String key() default "";// 最大次数 int num() default 5;// 过期时效 int duration() default 3;// 时间单位 TimeUnit timeUnit() default TimeUnit.MINUTES;}
2.自定义切面
package com.mimiwang.common.aop;import com.mimiwang.common.annotation.Limit;import com.mimiwang.common.constant.HttpStatus;import com.mimiwang.common.utils.NetworkUtils;import com.mimiwang.common.utils.ResponseUtil;import lombok.extern.slf4j.Slf4j;import org.apache.logging.log4j.util.Strings;import org.aspectj.lang.ProceedingJoinPoint;import org.aspectj.lang.annotation.Around;import org.aspectj.lang.annotation.Aspect;import org.aspectj.lang.annotation.Pointcut;import org.aspectj.lang.reflect.MethodSignature;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.data.redis.core.RedisTemplate;import org.springframework.stereotype.Component;import org.springframework.web.context.request.RequestContextHolder;import org.springframework.web.context.request.ServletRequestAttributes;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.IOException;import java.lang.reflect.Method;import java.util.concurrent.TimeUnit;@Aspect@Component@Slf4jpublic class LimitAspect { @Autowired RedisTemplateredisTemplate; @Pointcut("@annotation(com.mimiwang.common.annotation.Limit) || @within(com.mimiwang.common.annotation.Limit)") void test(){ } @Around("test()") public Object around(ProceedingJoinPoint joinPoint) throws Throwable { MethodSignature signature = (MethodSignature) joinPoint.getSignature(); Method method = signature.getMethod();// 获取方法注解 Limit annotation = method.getAnnotation(Limit.class);// 获取注解参数 String preKey = annotation.key(); int maxNum = annotation.num(); int duration = annotation.duration(); TimeUnit timeUnit = annotation.timeUnit();// 如果没有设置key,则默认 key=类名:方法名:ip if(Strings.isBlank(preKey)){ preKey=signature.getDeclaringTypeName()+":"+method.getName(); }// 获取ServletRequestAttributes ServletRequestAttributes requestAttributes =(ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); HttpServletRequest request = requestAttributes.getRequest(); HttpServletResponse response = requestAttributes.getResponse();// 获取ip String ip = NetworkUtils.getIpAddress(request); String key= preKey+":"+ip;// redis获取 Object o = redisTemplate.opsForValue().get(key);// 第一次访问 if(o == null){// 设置redis redisTemplate.opsForValue().set(key,1,duration,timeUnit); log.info("[Limit] [key]:{} | [Num]:{} | Status:{} | Max:{}",key,1,"ok",maxNum);// 放行 return joinPoint.proceed(joinPoint.getArgs()); }else { int num=(int)o;// 达到最大次数,禁止访问 if( num == maxNum){ log.error("[Limit] [key]:{} | [Num]:{} | Status:{} | Max:{}",key,num,"Ban",maxNum); return this.returnLimit(); }// 未达到最大值, 值+1 Long increment = redisTemplate.opsForValue().increment(key); log.info("[Limit] [key]:{} | [Num]:{} | Status:{} | Max:{}",key,increment,"ok",maxNum);// 放行 return joinPoint.proceed(joinPoint.getArgs()); } }// 错误响应 public String returnLimit() throws IOException { HttpStatus MAXLIMIT = HttpStatus.MAXLIMIT; return ResponseUtil.errorJSONData(MAXLIMIT.getCode(), MAXLIMIT.getMsg()); }}
3.解决依赖问题
此处说明
(1).HttpStatus.MAXLIMIT
枚举类型
public enum HttpStatus { MAXLIMIT(403,"限制最大访问次数"); private String msg; private int code; private HttpStatus(int code, String msg) { this.code=code; this.msg=msg; } public int getCode(){ return this.code; } public String getMsg(){ return this.msg; }}
(2).NetworkUtils
自定义Utils,用于获取请求ip
public class NetworkUtils { 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.getHeader("HTTP_CLIENT_IP"); } if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getHeader("HTTP_X_FORWARDED_FOR"); } if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getRemoteAddr(); } // 如果是多级代理,那么取第一个ip为客户端ip if (ip != null && ip.contains(",")) { ip = ip.substring(0, ip.indexOf(",")).trim(); } return ip; }}
(3).ResponseUtil
基于fastjson的自定义响应包装类
import com.alibaba.fastjson.JSONObject;public class ResponseUtil extends JSONObject { public static BaseUtil successJSONObject(String msg){ return successJSONObject(msg,null); } public static BaseUtil successJSONObject(String msg, Object data){ return successJSONObject(200,msg,data); } public static BaseUtil successJSONObject(int code,String msg, Object data){ return new BaseUtil(code, msg, data); } public static BaseUtil errorJSONObject(String msg){ return errorJSONObject(msg,null); } public static BaseUtil errorJSONObject(String msg, Object data){ return errorJSONObject(500,msg,data); } public static BaseUtil errorJSONObject(int code,String msg, Object data){ return new BaseUtil(code, msg, data); } public static String successJSONData(int code, String msg, Object data){ return toJSONString(new BaseUtil(code, msg, data)); } public static String successJSONData(String msg, Object data){ return toJSONString(new BaseUtil(200,msg,data)); } public static String successJSONData(Object data){ return toJSONString(new BaseUtil(200,"请求成功",data)); } public static String successJSONData(){ return toJSONString(new BaseUtil(200,"操作成功",null)); } public static String errorJSONData(int code, String msg, Object data){ return toJSONString(new BaseUtil(code,msg,data)); } public static String errorJSONData(String msg, Object data){ return toJSONString(new BaseUtil(500,msg,data)); } public static String errorJSONData(int code, String msg){ return toJSONString(new BaseUtil(code,msg,null)); } public static String errorJSONData(){ return toJSONString(new BaseUtil(500,"系统异常,操作失败!",null)); } public static String errorJSONData(String msg){ return toJSONString(new BaseUtil(500,msg,null)); } public static ClassgetResponseClass(){ return BaseUtil.class; } public static class BaseUtil{ private int code; private String msg; private Object data; public BaseUtil(int code, String msg, Object data) { this.code = code; this.msg = msg; this.data = data; } public int getCode() { return code; } public void setCode(int code) { this.code = code; } public String getMsg() { return msg; } public void setMsg(String msg) { this.msg = msg; } public Object getData() { return data; } public void setData(Object data) { this.data = data; } }}
4.使用
默认
@Limit()
@RestController@RequestMapping("/animal")public class AnimalController { @Autowired private ApplicationContext applicationContext; @Autowired IAnimalService animalService; @Autowired LoginAspect loginAspect; @Limit() @GetMapping("/info/id/{id}") public String getAnimalById(@PathVariable long id){ return ResponseUtil.successJSONData(animalService.selectAnimalById(id)); }}
自定义
@Limit(key = "aniInfo",num = 3,duration = 20,timeUnit = TimeUnit.SECONDS)
@RestController@RequestMapping("/animal")public class AnimalController { @Autowired IAnimalService animalService; @Limit(key = "aniInfo",num = 3,duration = 20,timeUnit = TimeUnit.SECONDS) @GetMapping("/info/id/{id}") public String getAnimalById(@PathVariable long id){ return ResponseUtil.successJSONData(animalService.selectAnimalById(id)); }}
5.运行错误可能的原因
(1).yaml没有配置redis
(2).LimitAspect没被扫描到
多模块下,A模块使用B模块Component,则A模块启动类需要加上@ComponentScan注解
参考:SpringBoot关于多模块调用其他模块自定义的bean_引用另一个模块的bean_要加油!的博客-CSDN博客