通过redis实现10秒内访问2次

2021-07-11  本文已影响0人  engineer_tang

1. 定义注解

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface VisitLimit {

    int expireTime() default 60;  //单位是秒

    int times() default 20;  //可访问次数
}

2. 添加面向切面的处理拦截器

package com.tangqiao.rainlyblog.common.component;

import com.tangqiao.rainlyblog.common.annotations.CheckSubmitForm;
import com.tangqiao.rainlyblog.common.annotations.VisitLimit;
import com.tangqiao.rainlyblog.common.config.AppConstantConfig;
import com.tangqiao.rainlyblog.common.utils.IpUtils;
import com.tangqiao.rainlyblog.common.utils.RedisConstant;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

@Aspect
@Component
public class VisitLimitAspect {

    @Autowired
    private RedisUtils redisUtils;

    @Autowired
    private AppConstantConfig appConstantConfig;

    /**
     * 定义切入点:对要拦截的方法进行定义与限制,如包、类
     *
     * 1、execution(public * *(..)) 任意的公共方法
     * 2、execution(* set*(..)) 以set开头的所有的方法
     * 3、execution(* com.lingyejun.annotation.LoggerApply.*(..))com.lingyejun.annotation.LoggerApply这个类里的所有的方法
     * 4、execution(* com.lingyejun.annotation.*.*(..))com.lingyejun.annotation包下的所有的类的所有的方法
     * 5、execution(* com.lingyejun.annotation..*.*(..))com.lingyejun.annotation包及子包下所有的类的所有的方法
     * 6、execution(* com.lingyejun.annotation..*.*(String,?,Long)) com.lingyejun.annotation包及子包下所有的类的有三个参数,第一个参数为String类型,第二个参数为任意类型,第三个参数为Long类型的方法
     * 7、execution(@annotation(com.lingyejun.annotation.Lingyejun))
     */
    @Pointcut("@annotation(com.tangqiao.rainlyblog.common.annotations.VisitLimit)")
    private void cutMethod() {

    }

    /**
     * 前置通知:在目标方法执行前调用
     */
    @Before("cutMethod()")
    public void begin(JoinPoint joinPoint) throws NoSuchMethodException {
        VisitLimit visitLimit = getDeclaredAnnotation(joinPoint);
        long limitExpireTime = visitLimit.expireTime();
        int limitTimes = visitLimit.times();
        String ipAddr = IpUtils.getIpAddr();
        String key = RedisConstant.BLOG_VISIT_LIMIT_PREFIX + ipAddr;
        Long times = redisUtils.incr(key);
        if(Objects.equals(times, 1L)) {
            redisUtils.set(key, "1", limitExpireTime, TimeUnit.SECONDS);
        } else {
            if (times > limitTimes) {
                throw new IllegalArgumentException("超过了访问次数");
            }
        }
    }

    /**
     * 获取方法中声明的注解
     *
     * @param joinPoint
     * @return
     * @throws NoSuchMethodException
     */
    public VisitLimit getDeclaredAnnotation(JoinPoint joinPoint) throws NoSuchMethodException {
        // 获取方法名
        String methodName = joinPoint.getSignature().getName();
        // 反射获取目标类
        Class<?> targetClass = joinPoint.getTarget().getClass();
        // 拿到方法对应的参数类型
        Class<?>[] parameterTypes = ((MethodSignature) joinPoint.getSignature()).getParameterTypes();
        // 根据类、方法、参数类型(重载)获取到方法的具体信息
        Method objMethod = targetClass.getMethod(methodName, parameterTypes);
        // 拿到方法定义的注解信息
        return objMethod.getDeclaredAnnotation(VisitLimit.class);
    }
}

3. IP获取工具类

package com.tangqiao.rainlyblog.common.utils;

import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.net.InetAddress;
import java.net.UnknownHostException;

public class IpUtils {

    /**
     * 获取当前网络ip
     * @return
     */
    public static String getIpAddr() {
        ServletRequestAttributes attr = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();
        HttpServletRequest request = attr.getRequest();
        String ipAddress = request.getHeader("x-forwarded-for");
        if(ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
            ipAddress = request.getHeader("Proxy-Client-IP");
        }
        if(ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
            ipAddress = request.getHeader("WL-Proxy-Client-IP");
        }
        if(ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
            ipAddress = request.getRemoteAddr();
            if(ipAddress.equals("127.0.0.1") || ipAddress.equals("0:0:0:0:0:0:0:1")){
                //根据网卡取本机配置的IP
                InetAddress inet=null;
                try {
                    inet = InetAddress.getLocalHost();
                } catch (UnknownHostException e) {
                    e.printStackTrace();
                }
                ipAddress= inet.getHostAddress();
            }
        }
        //对于通过多个代理的情况,第一个IP为客户端真实IP,多个IP按照','分割
        if(ipAddress!=null && ipAddress.length()>15){ //"***.***.***.***".length() = 15
            if(ipAddress.indexOf(",")>0){
                ipAddress = ipAddress.substring(0,ipAddress.indexOf(","));
            }
        }
        return ipAddress;
    }
}

4. Redis常量类

public class RedisConstant {

    public static final String BLOG_CATEGORY_PREFIX = "blog:category:detail:";

    public static final String BLOG_VISIT_LIMIT_PREFIX = "blog:visit:limit:";

    public static final String BLOG_CHECK_FORM_SUBMIT = "blog:check:form:submit:";
}

5. 在控制层的接口方法上加上注解

    @VisitLimit(expireTime = 10, times = 2)
    @ApiOperation("获取分类详细信息")
    @GetMapping("/getDetail/{id}")
    public ResultObject<TbCategory> getDetail(@PathVariable Long id) {
        return ResultStateEnum.SUCCESS.toData(categoryService.getDetail(id));
//        log.info("ip: {}", IpUtils.getIpAddr());
//        log.info("login user: {}", getLoginUser());
//        return ResultStateEnum.SUCCESS.message();
    }

启动项目,然后连续访问该接口,可以看到如下效果:


image.png
上一篇下一篇

猜你喜欢

热点阅读