亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Spring boot通過AOP防止API重復(fù)請(qǐng)求代碼實(shí)例

 更新時(shí)間:2019年12月03日 15:16:57   作者:21Gram  
這篇文章主要介紹了Spring boot通過AOP防止API重復(fù)請(qǐng)求代碼實(shí)例,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下

這篇文章主要介紹了Spring boot通過AOP防止API重復(fù)請(qǐng)求代碼實(shí)例,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下

實(shí)現(xiàn)思路

基于Spring Boot 2.x

自定義注解,用來標(biāo)記是哪些API是需要監(jiān)控是否重復(fù)請(qǐng)求

通過Spring AOP來切入到Controller層,進(jìn)行監(jiān)控

檢驗(yàn)重復(fù)請(qǐng)求的Key:Token + ServletPath + SHA1RequestParas

  • Token:用戶登錄時(shí),生成的Token
  • ServletPath:請(qǐng)求的Path
  • SHA1RequestParas:將請(qǐng)求參數(shù)使用SHA-1散列算法加密

使用以上三個(gè)參數(shù)拼接的Key作為去判斷是否重復(fù)請(qǐng)求

由于項(xiàng)目是基于集群的,使用Redis存儲(chǔ)Key,而且redis的特性,key可以設(shè)定在規(guī)定時(shí)間內(nèi)自動(dòng)刪除。這里的這個(gè)規(guī)定時(shí)間,就是api在規(guī)定時(shí)間內(nèi)不能重復(fù)提交。

自定義注解(注解作用于Controller層的API)

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

切面邏輯

import com.gotrade.apirepeatrequest.annotation.NoRepeatSubmission;
import com.gotrade.apirepeatrequest.common.JacksonSerializer;
import com.gotrade.apirepeatrequest.model.Result;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;



@Slf4j
@Aspect
@Component
public class NoRepeatSubmissionAspect {

  @Autowired
  RedisTemplate<String, String> redisTemplate;

  /**
   * 環(huán)繞通知
   * @param pjp
   * @param ars
   * @return
   */
  @Around("execution(public * com.gotrade.apirepeatrequest.controller..*.*(..)) && @annotation(ars)")
  public Object doAround(ProceedingJoinPoint pjp, NoRepeatSubmission ars) {
    ValueOperations<String, String> opsForValue = redisTemplate.opsForValue();
    try {
      if (ars == null) {
        return pjp.proceed();
      }

      HttpServletRequest request = ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();

      String token = request.getHeader("Token");
      if (!checkToken(token)) {
        return Result.failure("Token無效");
      }
      String servletPath = request.getServletPath();
      String jsonString = this.getRequestParasJSONString(pjp);
      String sha1 = this.generateSHA1(jsonString);

      // key = token + servlet path
      String key = token + "-" + servletPath + "-" + sha1;

      log.info("\n{\n\tServlet Path: {}\n\tToken: {}\n\tJson String: {}\n\tSHA-1: {}\n\tResult Key: {} \n}", servletPath, token, jsonString, sha1, key);

      // 如果Redis中有這個(gè)key, 則url視為重復(fù)請(qǐng)求
      if (opsForValue.get(key) == null) {
        Object o = pjp.proceed();
        opsForValue.set(key, String.valueOf(0), 3, TimeUnit.SECONDS);
        return o;
      } else {
        return Result.failure("請(qǐng)勿重復(fù)請(qǐng)求");
      }
    } catch (Throwable e) {
      e.printStackTrace();
      return Result.failure("驗(yàn)證重復(fù)請(qǐng)求時(shí)出現(xiàn)未知異常");
    }
  }

  /**
   * 獲取請(qǐng)求參數(shù)
   * @param pjp
   * @return
   */
  private String getRequestParasJSONString(ProceedingJoinPoint pjp) {
    String[] parameterNames = ((MethodSignature) pjp.getSignature()).getParameterNames();
    ConcurrentHashMap<String, String> args = null;

    if (Objects.nonNull(parameterNames)) {
      args = new ConcurrentHashMap<>(parameterNames.length);
      for (int i = 0; i < parameterNames.length; i++) {
        String value = pjp.getArgs()[i] != null ? pjp.getArgs()[i].toString() : "null";
        args.put(parameterNames[i], value);
      }
    }
    return JacksonSerializer.toJSONString(args);
  }

  private boolean checkToken(String token) {
    if (token == null || token.isEmpty()) {
      return false;
    }
    return true;
  }

  private String generateSHA1(String str){
    if (null == str || 0 == str.length()){
      return null;
    }
    char[] hexDigits = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
        'a', 'b', 'c', 'd', 'e', 'f'};
    try {
      MessageDigest mdTemp = MessageDigest.getInstance("SHA1");
      mdTemp.update(str.getBytes(StandardCharsets.UTF_8));

      byte[] md = mdTemp.digest();
      int j = md.length;
      char[] buf = new char[j * 2];
      int k = 0;
      for (int i = 0; i < j; i++) {
        byte byte0 = md[i];
        buf[k++] = hexDigits[byte0 >>> 4 & 0xf];
        buf[k++] = hexDigits[byte0 & 0xf];
      }
      return new String(buf);
    } catch (NoSuchAlgorithmException e) {
      e.printStackTrace();
    }
    return null;
  }
}

切面主要邏輯代碼,就是獲取request中相關(guān)的信息,然后再拼接成一個(gè)key;判斷在redis是否存在,不存在就添加并設(shè)置規(guī)定時(shí)間后自動(dòng)移除,存在就是重復(fù)請(qǐng)求 。

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • Java 深入探究講解抽象工廠模式

    Java 深入探究講解抽象工廠模式

    當(dāng)系統(tǒng)所提供的工廠所需生產(chǎn)的具體產(chǎn)品并不是一個(gè)簡(jiǎn)單的對(duì)象,而是多個(gè)位于不同產(chǎn)品等級(jí)結(jié)構(gòu)中屬于不同類型的具體產(chǎn)品時(shí)需要使用抽象工廠模式,抽象工廠模式是所有形式的工廠模式中最為抽象和最具一般性的一種形態(tài)
    2022-04-04
  • springboot shardingjdbc與druid數(shù)據(jù)源沖突問題及解決

    springboot shardingjdbc與druid數(shù)據(jù)源沖突問題及解決

    這篇文章主要介紹了springboot shardingjdbc與druid數(shù)據(jù)源沖突問題及解決,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-06-06
  • Java集合之整體結(jié)構(gòu)

    Java集合之整體結(jié)構(gòu)

    Java中集合類是Java編程中使用最頻繁、最方便的類。接下來通過本文給大家介紹Java集合之整體結(jié)構(gòu),一起看看吧
    2016-05-05
  • Java圖論進(jìn)階之最小生成樹算法詳解

    Java圖論進(jìn)階之最小生成樹算法詳解

    最小生成樹(Minimum Spanning Tree)就是給定無向圖中,邊權(quán)重最小的生成樹,下面這篇文章主要給大家介紹了關(guān)于Java圖論進(jìn)階之最小生成樹算法的相關(guān)資料,文中通過圖文以及實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2023-01-01
  • Java時(shí)間工具類Date的常用處理方法

    Java時(shí)間工具類Date的常用處理方法

    在Java中獲取當(dāng)前時(shí)間,可以使用 java.util.Date 類和 java.util.Calendar 類完成。其中,Date 類主要封裝了系統(tǒng)的日期和時(shí)間的信息,下面將詳細(xì)介紹Date類的常用處理方法,需要的可以參考一下
    2022-05-05
  • Java?Web防止同一用戶同時(shí)登錄幾種常見的實(shí)現(xiàn)方式

    Java?Web防止同一用戶同時(shí)登錄幾種常見的實(shí)現(xiàn)方式

    在JavaWeb開發(fā)中,實(shí)現(xiàn)同一賬號(hào)同一時(shí)間只能在一個(gè)地點(diǎn)登錄的功能,主要目的是為了增強(qiáng)系統(tǒng)的安全性,防止用戶賬戶被他人惡意登錄或同時(shí)在多個(gè)設(shè)備上使用,這篇文章主要給大家介紹了關(guān)于Java?Web防止同一用戶同時(shí)登錄幾種常見的實(shí)現(xiàn)方式,需要的朋友可以參考下
    2024-08-08
  • Java中break、continue、return在for循環(huán)中的使用

    Java中break、continue、return在for循環(huán)中的使用

    這篇文章主要介紹了break、continue、return在for循環(huán)中的使用,本文是小編收藏整理的,非常具有參考借鑒價(jià)值,需要的朋友可以參考下
    2017-11-11
  • java基礎(chǔ)篇之Date類型最常用的時(shí)間計(jì)算(相當(dāng)全面)

    java基礎(chǔ)篇之Date類型最常用的時(shí)間計(jì)算(相當(dāng)全面)

    這篇文章主要給大家介紹了關(guān)于java基礎(chǔ)篇之Date類型最常用的時(shí)間計(jì)算的相關(guān)資料,Java中的Date類是用來表示日期和時(shí)間的類,它提供了一些常用的方法來處理日期和時(shí)間的操作,需要的朋友可以參考下
    2023-12-12
  • springboot使用dubbo和zookeeper代碼實(shí)例

    springboot使用dubbo和zookeeper代碼實(shí)例

    這篇文章主要介紹了springboot使用dubbo和zookeeper代碼實(shí)例,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-11-11
  • 解決mybatisPlus 中的field-strategy配置失效問題

    解決mybatisPlus 中的field-strategy配置失效問題

    這篇文章主要介紹了解決mybatisPlus 中的field-strategy配置失效問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧
    2021-02-02

最新評(píng)論