APIインタフェースブラシ


APIインタフェースブラシ
名前の通り、あるインタフェースの誰かがある時間内にN回しか要求できないようにしたい.プロジェクトでよく見られる問題は、連点ボタンが複数回要求されることです.以前はweb側でフォームが繰り返しコミットされていましたが、tokenで解決できます.上記の方法に加えて、前後端を合わせる方法.すべてバックエンドで制御されます.
げんり
あなたが要求したとき、サーバはredisを通じてあなたの要求回数を記録し、回数が制限を超えるとアクセスしません.redisに保存されているkeyは時効性があり、期限が切れると削除されます.
コード実装:
グリッドを少し高く見せるために、注釈をカスタマイズして実現します.@RequestLimit注記
import java.lang.annotation.*;

/**
 *           
 *
 * @Target           ,ElementType.METHOD      ,ElementType.TYPE     
 * (ElementType)   :
 *     1.CONSTRUCTOR:       
 *     2.FIELD:     
 *     3.LOCAL_VARIABLE:        
 *     4.METHOD:      
 *     5.PACKAGE:     
 *     6.PARAMETER:      
 *     7.TYPE:     、  (      )  enum  
 * @Retention    Annotation        :  Annotation        ,       ;
 *          class   ;   class    Annotation         ,
 *      class        (       class   ,  Annotation class         )。
 *     meta-Annotation    Annotation “    ”  。
 * (RetentionPoicy)   :
 *     1.SOURCE:       (      )
 *     2.CLASS: class     ( class  )
 *     3.RUNTIME:      (      )
 *
 * @Inherited
 *           ,@Inherited                。
 *        @Inherited   annotation       class,   annotation     class   。
 */
@Documented
@Inherited
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface RequestLimit {
    //   second   ,       maxCount  
    int second() default 1;
    int maxCount() default 1;
}

RequestLimitIntercept遮断器
ブロッキングをカスタマイズし、リクエストする前にリクエスト回数チェックを行います.
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import top.lrshuai.limit.annotation.RequestLimit;
import top.lrshuai.limit.common.ApiResultEnum;
import top.lrshuai.limit.common.Result;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.Method;
import java.util.concurrent.TimeUnit;

/**
 *     
 */
@Slf4j
@Component
public class RequestLimitIntercept extends HandlerInterceptorAdapter {

    @Autowired
    private RedisTemplate redisTemplate;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        /**
         * isAssignableFrom()     Class                Class               ,           
         * isAssignableFrom()              
         * instanceof              
         */
        if(handler.getClass().isAssignableFrom(HandlerMethod.class)){
            //HandlerMethod            ,  ,  ,   
            HandlerMethod handlerMethod = (HandlerMethod) handler;
            Method method = handlerMethod.getMethod();
            //            
            RequestLimit methodAnnotation = method.getAnnotation(RequestLimit.class);
            //           ,   controller      
            RequestLimit classAnnotation = method.getDeclaringClass().getAnnotation(RequestLimit.class);
            //                     ,       
            RequestLimit requestLimit = methodAnnotation != null?methodAnnotation:classAnnotation;
            if(requestLimit != null){
                if(isLimit(request,requestLimit)){
                    resonseOut(response,Result.error(ApiResultEnum.REQUST_LIMIT));
                    return false;
                }
            }
        }
        return super.preHandle(request, response, handler);
    }
    //        
    public boolean isLimit(HttpServletRequest request,RequestLimit requestLimit){
        //    redis   key ,           ,   sessionid     key,   app ,       ID        。
        String limitKey = request.getServletPath()+request.getSession().getId();
        //       ,           
        Integer redisCount = (Integer) redisTemplate.opsForValue().get(limitKey);
        if(redisCount == null){
            //     
            redisTemplate.opsForValue().set(limitKey,1,requestLimit.second(), TimeUnit.SECONDS);
        }else{
            if(redisCount.intValue() >= requestLimit.maxCount()){
                return true;
            }
            //     
            redisTemplate.opsForValue().increment(limitKey);
        }
        return false;
    }

    /**
     *       
     * @param response
     * @param result
     * @throws IOException
     */
    private void resonseOut(HttpServletResponse response, Result result) throws IOException {
        response.setCharacterEncoding("UTF-8");
        response.setContentType("application/json; charset=utf-8");
        PrintWriter out = null ;
        String json = JSONObject.toJSON(result).toString();
        out = response.getWriter();
        out.append(json);
    }
}

ブロッキングは書き終わりましたが、登録を追加しなければなりません.WebMvcConfig構成クラス
私のはSpringboot2.*なので、WebMvcConfigurerを実現するだけです.springboot1.*であれば、WebMvcConfigurerAdapterから継承し、addInterceptors()を書き直してカスタムブロックを追加すればいいです.
@Slf4j
@Component
public class WebMvcConfig implements WebMvcConfigurer {

    @Autowired
    private RequestLimitIntercept requestLimitIntercept;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        log.info("    ");
        registry.addInterceptor(requestLimitIntercept);
    }
}
Controller
制御層テストインタフェース、
使用方法:
  • 第1種:直接クラスに注釈@RequestLimit(maxCount = 5,second = 1)
  • を使用する
  • 第2種:注釈@RequestLimit(maxCount = 5,second = 1)
  • を方法的に用いる.
    maxCount最大リクエスト数、second代表時間、単位は秒
    デフォルトでは1秒以内で、インタフェースごとに1回しか要求できません.
    @RestController
    @RequestMapping("/index")
    @RequestLimit(maxCount = 5,second = 1)
    public class IndexController {
    
        /**
         * @RequestLimit       ,       
         * @return
         */
        @GetMapping("/test1")
        @RequestLimit
        public Result test(){
            //TODO ...
            return Result.ok();
        }
    
        /**
         * @RequestLimit      ,       
         * @return
         */
        @GetMapping("/test2")
        public Result test2(){
            //TODO ...
            return Result.ok();
        }
    }
    

    クラスとメソッドに@RequestLimit注記が同時にある場合は、メソッド上のパラメータに準じて、注釈が少し多いようです.
    コードアドレス
    完全なコード、次のアドレス
    Giteeアドレス:https://gitee.com/rstyro/spring-boot/tree/master/SpringBoot-limit
    Githubアドレス:https://github.com/rstyro/Springboot/tree/master/SpringBoot-limit