Table des matières
AOP
Maison base de données Redis Comment SpringBoot combine Aop+Redis pour empêcher la soumission répétée d'interfaces

Comment SpringBoot combine Aop+Redis pour empêcher la soumission répétée d'interfaces

May 31, 2023 am 10:40 AM
redis aop springboot

Dans les projets de développement réels, une interface exposée en externe fait souvent face à de nombreuses demandes. Expliquons le concept d'idempotence : L'impact de toute exécution multiple est le même que l'impact d'une seule exécution. Selon ce sens, le sens final est que l'impact sur la base de données ne peut être qu'une seule fois et ne peut pas être traité de manière répétée. Comment garantir son idempotence implique généralement les méthodes suivantes :

1 La base de données établit un index unique pour garantir qu'une seule donnée est finalement insérée dans la base de données.

2. Mécanisme de jeton. Obtenez un jeton avant chaque demande d'interface, puis ajoutez ce jeton au corps de l'en-tête de la demande la prochaine fois. Si la vérification réussit, le jeton est supprimé et la suivante. la demande est jugée à nouveau.

3. Le verrouillage pessimiste ou le verrouillage optimiste peut garantir que les autres SQL ne peuvent pas mettre à jour les données à chaque fois (lorsque le moteur de base de données est innodb, la condition de sélection doit être un index unique pour empêcher la table entière d'être verrouillée)

4, Interrogez d'abord puis jugez. Tout d'abord, demandez si les données existent dans la base de données. Si l'existence prouve que la demande a été faite, la demande sera directement rejetée. Si elle n'existe pas, cela prouve qu'il s'agit bien de la demande. première fois que vous entrez et la demande sera publiée directement.

Pourquoi devrions-nous empêcher la soumission répétée d’interfaces ?
Pour certaines interfaces d'opération sensibles, telles que les nouvelles interfaces de données et les interfaces de paiement, si l'utilisateur clique plusieurs fois de manière incorrecte sur le bouton d'envoi, ces interfaces seront demandées plusieurs fois, ce qui peut éventuellement conduire à des exceptions système.

Comment contrôler le front-end ?
Le front-end peut être contrôlé via js. Lorsque l'utilisateur clique sur le bouton de soumission,
1. Le bouton est configuré pour ne pas être cliquable pendant un certain nombre de secondes.
2. Une fois le bouton cliqué, une boîte de dialogue d'invite de chargement apparaît. évitez de cliquer à nouveau jusqu'à ce que la demande d'interface revienne.
3. Cliquez sur le bouton Puis passez à une nouvelle page

Cependant, n'oubliez pas de ne jamais faire confiance au comportement de l'utilisateur, car vous ne savez pas quelles opérations étranges l'utilisateur fera, donc le plus l'important est de le traiter sur le backend.

Utilisez aop+redis pour le traitement d'interception
1. Créez la classe d'aspect RepeatSubmitAspect
Processus d'implémentation : après la requête d'interface, le chemin du jeton+de la requête est utilisé comme valeur de clé pour lire les données dans redis. , cela prouve qu'il a été soumis à plusieurs reprises. Le contraire n'est pas vrai. S'il ne s'agit pas d'une soumission répétée, elle sera publiée directement, et la clé sera écrite dans Redis et configurée pour expirer dans un certain délai (j'ai défini une expiration de 5 secondes ici)


Dans les projets Web traditionnels, afin d'éviter les soumissions répétées, l'approche habituelle est la suivante : Le backend génère un jeton de soumission unique (uuid) et le stocke sur le serveur lorsque la page initie une requête, elle transporte le jeton après vérification. la demande pour garantir le caractère unique de la demande.
Cependant, la méthode d'appel nécessite des modifications à la fois au niveau du front et du back-end. Si elle est au début du projet, elle peut être réalisée. Cependant, à un stade ultérieur du projet, de nombreuses fonctions ont été mises en œuvre et c'est le cas. impossible de procéder à des changements à grande échelle.

Idée
1. Personnalisez l'annotation @NoRepeatSubmit pour marquer toutes les demandes soumises dans le contrôleur
2 Interceptez toutes les méthodes marquées avec @NoRepeatSubmit via AOP
3, obtenez le jeton de l'utilisateur actuel ou JSessionId+current. L'adresse de requête est utilisée comme clé unique pour obtenir le verrou distribué Redis. Si elle est obtenue simultanément, un seul thread peut l'obtenir.
4. Une fois l'activité exécutée, libérez le verrou

À propos du verrouillage distribué Redis
L'utilisation de Redis est destinée au déploiement d'équilibrage de charge. S'il s'agit d'un projet autonome, vous pouvez utiliser un cache thread-safe local pour remplacer Redis

. Code
Annotations personnalisées

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

/**
 * @ClassName NoRepeatSubmit
 * @Description 这里描述
 * @Author admin
 * @Date 2021/3/2 16:16
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface NoRepeatSubmit {

    /**
     * 设置请求锁定时间
     *
     * @return
     */
    int lockTime() default 10;

}
Copier après la connexion

AOP

package com.hongkun.aop;

/**
 * @ClassName RepeatSubmitAspect
 * @Description 这里描述
 * @Author admin
 * @Date 2021/3/2 16:15
 */

import com.hongkun.until.ApiResult;
import com.hongkun.until.Result;
import com.hongkun.until.RedisLock;
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.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * @author liucheng
 * @since 2020/01/15
 * 防止接口重复提交
 */
@Aspect
@Component
public class RepeatSubmitAspect {
    private static final Logger LOGGER = LoggerFactory.getLogger(RepeatSubmitAspect.class);

    @Autowired
    private RedisLock redisLock;

    @Pointcut("@annotation(noRepeatSubmit)")
    public void pointCut(NoRepeatSubmit noRepeatSubmit) {
    }

    @Around("pointCut(noRepeatSubmit)")
    public Object around(ProceedingJoinPoint pjp, NoRepeatSubmit noRepeatSubmit) throws Throwable {

        int lockSeconds = noRepeatSubmit.lockTime();

        RequestAttributes ra = RequestContextHolder.getRequestAttributes();
        ServletRequestAttributes sra = (ServletRequestAttributes) ra;
        HttpServletRequest request = sra.getRequest();

        Assert.notNull(request, "request can not null");

        // 此处可以用token或者JSessionId
        String token = request.getHeader("token");
        String path = request.getServletPath();
        String key = getKey(token, path);
        String clientId = getClientId();

        boolean isSuccess = redisLock.lock(key, clientId, lockSeconds,TimeUnit.SECONDS);
        LOGGER.info("tryLock key = [{}], clientId = [{}]", key, clientId);

        if (isSuccess) {
            LOGGER.info("tryLock success, key = [{}], clientId = [{}]", key, clientId);
            // 获取锁成功
            Object result;
            try {
                // 执行进程
                result = pjp.proceed();
            } finally {
                // 解锁
                redisLock.unlock(key, clientId);
                LOGGER.info("releaseLock success, key = [{}], clientId = [{}]", key, clientId);
            }

            return result;

        } else {
            // 获取锁失败,认为是重复提交的请求
            LOGGER.info("tryLock fail, key = [{}]", key);
            return ApiResult.success(200, "重复请求,请稍后再试", null);
        }

    }

    private String getKey(String token, String path) {
        return "00000"+":"+token + path;
    }

    private String getClientId() {
        return UUID.randomUUID().toString();
    }


}
Copier après la connexion

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn

Outils d'IA chauds

Undresser.AI Undress

Undresser.AI Undress

Application basée sur l'IA pour créer des photos de nu réalistes

AI Clothes Remover

AI Clothes Remover

Outil d'IA en ligne pour supprimer les vêtements des photos.

Undress AI Tool

Undress AI Tool

Images de déshabillage gratuites

Clothoff.io

Clothoff.io

Dissolvant de vêtements AI

AI Hentai Generator

AI Hentai Generator

Générez AI Hentai gratuitement.

Article chaud

R.E.P.O. Crystals d'énergie expliqués et ce qu'ils font (cristal jaune)
3 Il y a quelques semaines By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. Meilleurs paramètres graphiques
3 Il y a quelques semaines By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. Comment réparer l'audio si vous n'entendez personne
3 Il y a quelques semaines By 尊渡假赌尊渡假赌尊渡假赌
WWE 2K25: Comment déverrouiller tout dans Myrise
3 Il y a quelques semaines By 尊渡假赌尊渡假赌尊渡假赌

Outils chauds

Bloc-notes++7.3.1

Bloc-notes++7.3.1

Éditeur de code facile à utiliser et gratuit

SublimeText3 version chinoise

SublimeText3 version chinoise

Version chinoise, très simple à utiliser

Envoyer Studio 13.0.1

Envoyer Studio 13.0.1

Puissant environnement de développement intégré PHP

Dreamweaver CS6

Dreamweaver CS6

Outils de développement Web visuel

SublimeText3 version Mac

SublimeText3 version Mac

Logiciel d'édition de code au niveau de Dieu (SublimeText3)

Pourquoi une erreur se produit-elle lors de l'installation d'une extension à l'aide de PECL dans un environnement Docker? Comment le résoudre? Pourquoi une erreur se produit-elle lors de l'installation d'une extension à l'aide de PECL dans un environnement Docker? Comment le résoudre? Apr 01, 2025 pm 03:06 PM

Causes et solutions pour les erreurs Lors de l'utilisation de PECL pour installer des extensions dans un environnement Docker Lorsque nous utilisons un environnement Docker, nous rencontrons souvent des maux de tête ...

Comment démarrer efficacement plusieurs services dans Dockerfile? Comment démarrer efficacement plusieurs services dans Dockerfile? Apr 01, 2025 pm 02:15 PM

À propos de l'utilisation efficace des commandes CMD dans Dockerfile De nombreux nouveaux utilisateurs de Docker utilisent CMD ...

Comment résoudre le problème de conversion de type user_id lors de l'utilisation du flux redis pour implémenter les files d'attente de messages dans le langage Go? Comment résoudre le problème de conversion de type user_id lors de l'utilisation du flux redis pour implémenter les files d'attente de messages dans le langage Go? Apr 02, 2025 pm 04:54 PM

Le problème de l'utilisation de Redessstream pour implémenter les files d'attente de messages dans le langage GO consiste à utiliser le langage GO et redis ...

Comparaison des files d'attente Redis et de la stabilité de MySQL: Pourquoi Redis est-il sujet à la perte de données? Comparaison des files d'attente Redis et de la stabilité de MySQL: Pourquoi Redis est-il sujet à la perte de données? Apr 01, 2025 pm 02:24 PM

Comparaison des files d'attente Redis et de la stabilité de MySQL: Pourquoi Redis est-il sujet à la perte de données? Dans l'environnement de développement, en utilisant des cadres Php7.2 et ThinkPhp, nous sommes souvent confrontés au choix de la coopération ...

Comment vérifier efficacement la validité des connexions redis dans le projet Laravel6? Comment vérifier efficacement la validité des connexions redis dans le projet Laravel6? Apr 01, 2025 pm 02:00 PM

Comment vérifier la validité des connexions Redis dans les projets Laravel6 est un problème courant, en particulier lorsque les projets s'appuient sur Redis pour le traitement des entreprises. Ce qui suit est ...

Comment appliquer Debian Strings sur un site Web Comment appliquer Debian Strings sur un site Web Apr 02, 2025 am 08:21 AM

Cet article explique comment optimiser les performances du site Web sur Debian Systems. "Debianstrings" n'est pas un terme standard et peut se référer aux outils ou technologies utilisés dans Debian Systems pour améliorer les performances du site Web. Voici quelques conseils pratiques: 1. Il est recommandé d'utiliser le panneau Pagoda pour simplifier le processus d'installation et de configuration pour le serveur Web et la configuration de l'environnement PHP. Il est recommandé d'installer Nginx1.22.1 en tant que serveur Web, PHP8.2 comme interprète de script et MySQL10.7.3-MariaDB comme système de base de données. Assurez-vous d'activer les extensions de PHP nécessaires, telles que FileInfo, Opcache, Memcached, Red

See all articles