日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

充值核销卡密恶意并发请求防止重复利用卡密充值成功解决方案

發(fā)布時間:2023/12/10 编程问答 22 豆豆
生活随笔 收集整理的這篇文章主要介紹了 充值核销卡密恶意并发请求防止重复利用卡密充值成功解决方案 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

項目場景:

springboot項目 核銷卡密充值 在并發(fā)測試中一個卡密重復充值成功

問題描述:

第一次寫沒考慮那么多 就在業(yè)務層加了幾個校驗卡密狀態(tài)邏輯 只要卡密的狀態(tài)沒問題就直接修改用戶的余額

if(ObjectUtil.isNull(cards)){return failure("該卡密不存在");}if(cards.getIsEnable() == false){return failure("該卡密已被禁用,請聯(lián)系管理員");}if(cards.getStatus() == 3){return failure("該卡密已被使用,如余額未增加,請聯(lián)系管理員");} <update id="updateUserLeftAmount">UPDATE sys_user SETleft_amount = #{add}where id = #{id} </update>

結果:

20個并發(fā)請求的情況下 沖100快 直接沖進去了900快 因為接口被大量的重復數(shù)據(jù)請求


解決方案:

既然是接口被重復請求, 那我就直接加個aop攔截接口,防止重復請求總行了把


切面邏輯 校驗相同ip對這個接口的請求次數(shù)

@Aspect @Component public class LimitRequestAspect {private static ConcurrentHashMap<String, ExpiringMap<String, Integer>> book = new ConcurrentHashMap<>();// 定義切點// 讓所有有@LimitRequest注解的方法都執(zhí)行切面方法@Pointcut("@annotation(limitRequest)")public void excudeService(LimitRequest limitRequest) {}@Around("excudeService(limitRequest)")public Object doAround(ProceedingJoinPoint pjp, LimitRequest limitRequest) throws Throwable {// 獲得request對象RequestAttributes ra = RequestContextHolder.getRequestAttributes();ServletRequestAttributes sra = (ServletRequestAttributes) ra;HttpServletRequest request = sra.getRequest();// 獲取Map對象, 如果沒有則返回默認值// 第一個參數(shù)是key, 第二個參數(shù)是默認值ExpiringMap<String, Integer> uc = book.getOrDefault(request.getRequestURI(), ExpiringMap.builder().variableExpiration().build());Integer uCount = uc.getOrDefault(request.getRemoteAddr(), 0);if (uCount >= limitRequest.count()) { // 超過次數(shù),不執(zhí)行目標方法throw new BaseException("請勿頻繁請求"); // return "接口請求超過次數(shù)";} else if (uCount == 0){ // 第一次請求時,設置有效時間 // /** Expires entries based on when they were last accessed */ // ACCESSED, // /** Expires entries based on when they were created */ // CREATED;uc.put(request.getRemoteAddr(), uCount + 1, ExpirationPolicy.CREATED, limitRequest.time(), TimeUnit.MILLISECONDS);} else { // 未超過次數(shù), 記錄加一uc.put(request.getRemoteAddr(), uCount + 1);}book.put(request.getRequestURI(), uc);// result的值就是被攔截方法的返回值Object result = pjp.proceed();return result;}

注解類

@Documented @Target(ElementType.METHOD) // 說明該注解只能放在方法上面 @Retention(RetentionPolicy.RUNTIME) public @interface LimitRequest {long time() default 6000; // 限制時間 單位:毫秒int count() default 1; // 允許請求的次數(shù) }

結果:

雖然加了接口請求次數(shù)攔截,但并不能防止并發(fā)使用這個卡密充值重復的問題,然后大佬告訴我 加個鎖吧 然后發(fā)了張圖片給我

然后我根據(jù)圖片的描述加了個version字段 修改了一下我的sql


<update id="updateUserLeftAmount">UPDATE sys_user SETleft_amount = #{add},version = version+1where left_amount = #{leftAmount} and id = #{id} and version=#{version}</update>

再次測試:

還是會出現(xiàn)重復充值的情況

解決方案:

請教大佬 大佬又發(fā)了兩張圖給我看 是關于redis的 讓我利用redis的單線程機制來校驗

然后我集成了jedis又修改了億點點校驗邏輯 代碼如下

在修改用戶余額的操作前使用redis的setnx操作, 存入用戶的唯一id 再設置一個過期時間 然后再expire查詢一下是否存在 如果存在就return

JedisUtil jedisUtil = JedisUtil.getInstance();Long verifyCarmi = jedisUtil.setnxWithTimeOut("verifyCarmi", user.getId(), 10);if(verifyCarmi == 0){map.put("msg","請勿重復充值");map.put("bool",false);return map;}

jedisUtil.setnxWithTimeOut的代碼

/*** 添加一個鍵值對,如果鍵存在不在添加,如果不存在,添加完成以后設置鍵的有效期* @param key* @param value* @param timeOut*/public Long setnxWithTimeOut(String key,String value,int timeOut){Jedis jedis = getJedis();long expire = 2;if(0!=jedis.setnx(key, value)){expire = jedis.expire(key, timeOut);}returnJedis(jedis);return expire;}

再次測試:

沒問題了 ,并發(fā)請求下只能充值一次全給redis的校驗return了
redis真好用😊
感謝大佬 學會了學會了

總結

以上是生活随笔為你收集整理的充值核销卡密恶意并发请求防止重复利用卡密充值成功解决方案的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。