[JAVA] Implémentons une fonction pour limiter le nombre d'accès à l'API avec SpringBoot + Redis

introduction

Pour l'instant, je pense qu'il y a beaucoup de cas d'utilisation de l'interface API. Cependant, si l'API est appelée trop de fois, le traitement côté serveur peut être retardé. Cela signifie que quelque chose de mauvais va se passer. Cette fois, afin de limiter le nombre d'accès à l'API, j'ai écrit cet article après un examen et une réflexion approfondis.

environnement

Vous devez avoir à la fois des connaissances Spring Boot et des connaissances Redis.

Méthode

En tant que politique de mise en œuvre, elle est implémentée sous forme d'annotation principalement à l'aide de Spring Boot et Redis. Vous pouvez voir les détails en regardant le commentaire.

1. Créez une classe d'annotation

AccessLimit.java


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

import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

/**
 * @author Hyman
 * @date 2019/12/25 11:12
 */
@Retention(RUNTIME)
@Target(METHOD)
public @interface AccessLimit {

    //Limite de temps(Secondes)
    int seconds();
    //Nombre maximum d'accès
    int maxCount();
    //Statut de connexion
    boolean needLogin() default true;
}

2. Créez une classe d'intercepteur

ApiLimitInterceptor.java


import com.alibaba.fastjson.JSON;
import com.example.demo.action.AccessLimit;
import com.example.demo.redis.RedisService;
import com.example.demo.result.CodeMsg;
import com.example.demo.result.Result;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.OutputStream;

/**
 * @author Hyman
 * @date 2019/12/25 11:22
 */
@Component
public class ApiLimitInterceptor extends HandlerInterceptorAdapter {

    @Autowired
    private RedisService redisService;

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

        //Découvrez si la requête est une HandlerMethod
        if(handler instanceof HandlerMethod){

            HandlerMethod hm = (HandlerMethod) handler;

            //Obtenez l'annotation au-dessus de la méthode et voyez s'il existe une annotation AccessLimit
            AccessLimit accessLimit = hm.getMethodAnnotation(AccessLimit.class);
            if(accessLimit == null){
         //Ne rien faire sans l'annotation AccessLimit
                return true;
            }
       
            //Obtenez la limite de temps
            int seconds = accessLimit.seconds();
            //Obtenez le nombre maximum d'accès
            int maxCount = accessLimit.maxCount();
            boolean login = accessLimit.needLogin();
            String key = request.getRequestURI();

            //Si une connexion est requise, procédez comme suit:
            if(login){
                //Je vais omettre la partie authentification ici
                // key+=""+"userInfo";
            }

            //Obtenez le nombre d'accès de Redis
            AccessKey ak = AccessKey.withExpire(seconds);
            Integer count = redisService.get(ak,key,Integer.class);

       //Créez une branche et effectuez chaque processus
            if(count == null){
                //Lors de l'accès pour la première fois
                redisService.set(ak,key,1);
            }else if(count < maxCount){
                //Nombre d'accès + 1
                redisService.incr(ak,key);
            }else{
                //Si le nombre maximum d'accès est dépassé, un message d'erreur sera créé et renvoyé.
                render(response,"Le nombre maximum d'accès sera dépassé dans le délai spécifié."); //
                return false;
            }
        }

        return true;

    }
    
 /**
  *Si le nombre maximum d'accès est dépassé, un message d'erreur sera créé et renvoyé.
  */
  private void render(HttpServletResponse response, CodeMsg cm)throws Exception {
        response.setContentType("application/json;charset=UTF-8");
        OutputStream out = response.getOutputStream();
        String str  = JSON.toJSONString(Result.error(cm));
        out.write(str.getBytes("UTF-8"));
        out.flush();
        out.close();
    }
}

3. Placez la classe intercepteur

Enregistrez la classe d'intercepteur créée dans 2 ci-dessus dans Spring Boot.

WebConfig.java


import com.example.demo.ExceptionHander.ApiLimitInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;

/**
 * @author Hyman
 * @date 2019/11/31 15:58
 */
@Configuration
public class WebConfig extends WebMvcConfigurerAdapter {

    @Autowired
    private ApiLimitInterceptor interceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(interceptor);
    }
}

4. Créez un contrôleur

J'ai créé un contrôleur et mis @AccessLimit au-dessus de la méthode. Une fois que c'est fait, testons avec Postman.

ApiLimitController.java


import com.example.demo.result.Result;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

/**
 * @author Hyman
 * @date 2019/12/25 13:21
 */
@Controller
public class ApiLimitController {

    //Temps: 5 secondes,Nombre maximum d'accès:5 ,Statut de connexion: obligatoire
    @AccessLimit(seconds=5, maxCount=5, needLogin=true)
    @RequestMapping("/ApiLimitTest")
    @ResponseBody
    public Result<String> ApiLimitTest(){
        return Result.success("Demande réussie!");
    }
 }

finalement

Merci d'avoir lu jusqu'au bout. N'hésitez pas à signaler les parties qui vous semblent étranges. Je vous remercie.

Recommended Posts

Implémentons une fonction pour limiter le nombre d'accès à l'API avec SpringBoot + Redis
Essayez d'implémenter une fonction de connexion avec Spring-Boot
Comment accéder directement à Socket avec la fonction TCP de Spring Integration
J'ai essayé d'implémenter une fonction équivalente à Felica Lite avec HCE-F d'Android
Écrivons une fonction Lambda qui intègre Amazon API Gateway à Spring Cloud Function.
Écrivons comment créer une API avec SpringBoot + Docker à partir de 0
Trouvez le nombre de jours dans un mois avec Kotlin
Essayez d'imiter l'idée d'un tableau à deux dimensions avec un tableau à une dimension
J'ai essayé d'exprimer les résultats avant et après de la classe Date avec une ligne droite numérique
J'ai essayé d'implémenter la fonction de prévisualisation d'image avec Rails / jQuery
Une histoire sur l'utilisation de l'API League Of Legends avec JAVA
[Illustration] Recherche de la somme des pièces avec une fonction récursive [Ruby]
Je voulais écrire un processus équivalent à une instruction while avec l'API Java 8 Stream
L'histoire de la création d'un lanceur de jeu avec une fonction de chargement automatique [Java]
Exprimons le résultat de l'analyse du code d'octet Java dans un diagramme de classes
Comment convertir un tableau de chaînes en un tableau d'objets avec l'API Stream
[Rails] Implémentez la fonction d'achat de produits avec une carte de crédit enregistrée auprès de PAY.JP
J'ai essayé de visualiser l'accès de Lambda → Athena avec AWS X-Ray
Comment mettre en œuvre la fonction d'authentification du courrier au moment de l'inscription de l'utilisateur
Comment déterminer le nombre de parallèles
Comment implémenter TextInputLayout avec la fonction de validation
Comment obtenir l'ID d'un utilisateur qui s'est authentifié avec Firebase dans Swift
Envoyez des notifications à Slack avec la version gratuite de sentry (en utilisant lambda)
Faire une marge à gauche du TextField
Écraser le contenu de la configuration avec Spring-boot + JUnit5
Comment mettre en œuvre la fonction de chapelure avec Gretel
[Android] Implémentez rapidement la fonction pour afficher le mot de passe
Essayez d'implémenter la fonction de connexion avec Spring Boot
[Pour les débutants] Comment implémenter la fonction de suppression
Faisons une fonction de recherche avec Rails (ransack)
Comment créer un serveur Jenkins avec un conteneur Docker sur CentOS 7 de VirtualBox et accéder au serveur Jenkins à partir d'un PC local
Premiers pas avec Doma - Introduction à l'API Criteria
J'ai essayé de créer un outil de comparaison des prix des produits Amazon dans le monde entier avec Java, l'API Amazon Product Advertising, l'API Currency (29/01/2017)
J'ai vérifié le nombre de taxis avec Ruby
J'ai essayé de vérifier le fonctionnement de la requête http (Put) avec Talented API Tester
Comment définir une limite de relance pour sidekiq et notifier les files d'attente mortes avec Slack
J'ai créé une fonction pour enregistrer des images avec l'API dans Spring Framework. Partie 1 (édition API)
3. Créez une base de données à laquelle accéder à partir du module Web
Comment exécuter l'application SpringBoot en tant que service
Accédez au h2db intégré de Spring Boot avec jdbcTemplate
L'histoire de la création d'un proxy inverse avec ProxyServlet
Implémentez iOS14 UICollectionView avec le code minimum requis.
Limiter le nombre de threads à l'aide du service d'exécution de Java
Pourquoi implémenter avec singleton au lieu de la méthode statique
Je souhaite ajouter une fonction de suppression à la fonction de commentaire
L'histoire du transfert d'un conteneur Docker vers le registre de packages GitHub et Docker Hub avec des actions GitHub
Comment gérer la différence dans chaque environnement avec yml sans augmenter le nombre de RAILS_ENV
Implémentation d'une API forte pour "Je veux afficher ~~ à l'écran" avec un simple CQRS
Découvrons comment recevoir avec Request Body avec l'API REST de Spring Boot
Une histoire sur un étudiant qui a été épargné en raison de l'épidémie de SRAS-CoV-2 et a finalement créé une application Twitter et a finalement atteint la limite de l'API Twitter.