亚洲激情专区-91九色丨porny丨老师-久久久久久久女国产乱让韩-国产精品午夜小视频观看

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Springboot中怎么利用AOP防止API重復請求

發布時間:2021-08-02 16:04:14 來源:億速云 閱讀:142 作者:Leah 欄目:編程語言

Springboot中怎么利用AOP防止API重復請求,很多新手對此不是很清楚,為了幫助大家解決這個難題,下面小編將為大家詳細講解,有這方面需求的人可以來學習下,希望你能有所收獲。

實現思路

基于Spring Boot 2.x

自定義注解,用來標記是哪些API是需要監控是否重復請求

通過Spring AOP來切入到Controller層,進行監控

檢驗重復請求的Key:Token + ServletPath + SHA1RequestParas

Token:用戶登錄時,生成的Token  ServletPath:請求的Path  SHA1RequestParas:將請求參數使用SHA-1散列算法加密

使用以上三個參數拼接的Key作為去判斷是否重復請求

由于項目是基于集群的,使用Redis存儲Key,而且redis的特性,key可以設定在規定時間內自動刪除。這里的這個規定時間,就是api在規定時間內不能重復提交。

自定義注解(注解作用于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@Componentpublic class NoRepeatSubmissionAspect {  @Autowired  RedisTemplate<String, String> redisTemplate;  /**   * 環繞通知   * @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中有這個key, 則url視為重復請求      if (opsForValue.get(key) == null) {        Object o = pjp.proceed();        opsForValue.set(key, String.valueOf(0), 3, TimeUnit.SECONDS);        return o;      } else {        return Result.failure("請勿重復請求");      }    } catch (Throwable e) {      e.printStackTrace();      return Result.failure("驗證重復請求時出現未知異常");    }  }  /**   * 獲取請求參數   * @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中相關的信息,然后再拼接成一個key;判斷在redis是否存在,不存在就添加并設置規定時間后自動移除,存在就是重復請求 。

看完上述內容是否對您有幫助呢?如果還想對相關知識有進一步的了解或閱讀更多相關文章,請關注億速云行業資訊頻道,感謝您對億速云的支持。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

河源市| 遵义县| 诏安县| 江都市| 夏津县| 普兰店市| 拜城县| 施甸县| 石城县| 积石山| 石门县| 镶黄旗| 汝阳县| 泸水县| 峨边| 莲花县| 广水市| 彝良县| 宣汉县| 德钦县| 江北区| 兴山县| 新邵县| 江都市| 津南区| 米泉市| 乐至县| 太康县| 凌源市| 克拉玛依市| 连平县| 美姑县| 乌海市| 桐梓县| 宁化县| 旬邑县| 茌平县| 察雅县| 灯塔市| 达州市| 石渠县|