Aop+自定义注解实现接口限制

目录

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)
@Documented
public @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
@Slf4j
public class LimitAspect {

    @Autowired
    RedisTemplate<String,Object> redisTemplate;

    @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 Class<BaseUtil> getResponseClass(){
        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博客

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值