redis限时业务应用(二)

redis限时业务应用(二)

每天点滴努力,成就编程路!

限时业务的应用;一般情景会是验证码,二维码生存周期,接口api防刷,订单重复提交问题。关于验证码(例如邮件验证码的有效时间),云之讯会有专门的验证api。订单重复提交问题,用用户id+订单商品信息id作为key值设置有效时间(极短)。接口防止刷,是本节讲述的重点。

自定义防止刷注解

加上此注解表示防刷

package com.sise.demo1.demo.common.config.annotation;

import java.lang.annotation.*;

/**
 * author zxq
 * date 2020/8/5 22:22
 * 描述 自定义注解类
 * fans
 */

@Inherited
@Documented
@Target({ElementType.FIELD,ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface AccessLimit {

    //接口请求次数
    int times() default 2;

    //时间范围
    int second() default 5;


}

定义拦截器

当加上@AccessLimit 便进行防刷限制。

package com.sise.demo1.demo.common.interceptor;

import com.sise.demo1.demo.common.config.annotation.AccessLimit;
import com.sise.demo1.demo.common.utils.IPUtils;
import com.sise.demo1.demo.common.utils.RequestUtils;

import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.util.concurrent.TimeUnit;

/**
 * author zxq
 * date 2020/8/5 22:29
 * 拦截器:拦截在某一时间内,请求太多次。
 */
@Component
public class AccessLimitInterceptor implements HandlerInterceptor {


    @Resource
    private RedisTemplate<String, Object> redisTemplate;



    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response,Object handler) throws Exception{

        try{
            System.out.println("我被执行了 AccessLimitInterceptor");
            if(handler instanceof HandlerMethod){
                //强转
                HandlerMethod handlerMethod = (HandlerMethod) handler;
                // 获取方法
                Method method = handlerMethod.getMethod();
                // 是否有AccessLimit注解
                if(!method.isAnnotationPresent(AccessLimit.class)){
                    return true;
                }
                // 获取注解内容信息
                AccessLimit accessLimit = method.getAnnotation(AccessLimit.class);
                if(accessLimit == null){
                    return true;
                }
                int times = accessLimit.times();//请求次数
                int second = accessLimit.second();//请求时间范围
                //根据 IP + API 限流
                String key = IPUtils.getIpAddr(request) + request.getRequestURI();
                //根据key获取已请求次数
                Integer maxTimes = (Integer)redisTemplate.opsForValue().get(key);
                if(maxTimes == null){
                    //set时一定要加过期时间
                    redisTemplate.opsForValue().set(key, 1, second, TimeUnit.SECONDS);
                }else if(maxTimes < times){
                    redisTemplate.opsForValue().set(key, maxTimes+1, second, TimeUnit.SECONDS);
                }else{
                    // 30405 API_REQUEST_TOO_MUCH  请求过于频繁
                    RequestUtils.out(response,"请求过于频繁");
                    return false;
                }
            }

        }catch (Exception e){
            return false;
        }
        return true;

    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {

    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {

    }


}

RequestUtils统一返回

package com.sise.demo1.demo.common.utils;

import com.alibaba.fastjson.JSONObject;


import javax.servlet.ServletResponse;
import java.io.PrintWriter;

/**
 * author zxq
 * date 2020/8/5 22:44
 */
public class RequestUtils {

    public static void out(ServletResponse response, Object object) {
        PrintWriter out = null;
        try {
            response.setContentType("application/json;charset=UTF-8");
            response.setCharacterEncoding("UTF-8");
            JSONObject jsonObject = new JSONObject();
            jsonObject.put("state",200);
            jsonObject.put("msg","请求过于频繁");
            jsonObject.put("data",null);
            out = response.getWriter();
            out.append(jsonObject.toString());
        } catch (Exception e) {
            System.out.println("exception");
        } finally {
            if (null != out) {
                out.flush();
                out.close();
            }
        }
    }

}

应用拦截器

package com.sise.demo1.demo.common.config;

import com.sise.demo1.demo.common.interceptor.AccessLimitInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

/**
 * author zxq
 * date 2020/8/5 22:52
 */
@Configuration
public class WebConfig implements WebMvcConfigurer {

    //这里需要注入拦截器 否则无法获取到拦截器注入的RedisTemplate redisTemplate;
    @Bean
    public AccessLimitInterceptor accessLimitInterceptor(){
        return new AccessLimitInterceptor();
    }


    @Override
    public void addInterceptors(InterceptorRegistry registry) {

        //API限流拦截
        registry.addInterceptor(accessLimitInterceptor()).addPathPatterns("/**").excludePathPatterns("/static/**");

    }
}

应用例子

redis限时业务应用(二)_第1张图片

运行截图

redis限时业务应用(二)_第2张图片

如有错误,或有更好的想法,请联系我,感激不尽!

你可能感兴趣的:(Java)