大佬們對于api并發(fā)時(shí)防止重復(fù)提交有什么好的方案嗎?
并發(fā)和重復(fù)提交是兩個(gè)不同概念吧。
并發(fā)不會導(dǎo)致重復(fù)提交;
針對多次點(diǎn)擊等等的,簡單方式是前端限制;
如果是被刷接口,那就要看你的業(yè)務(wù)性質(zhì),是登錄用戶才能使用還是所有人能使用;
如果登錄用戶才能使用:
根據(jù)UID生成一個(gè)form的token,一個(gè)UID對應(yīng)一個(gè)token,每次提交都把token重置,然后驗(yàn)證token是否最新的即可。
如果不限制用戶的:
根據(jù)用戶IP地址結(jié)合瀏覽器指紋進(jìn)行限制,但這種都不是100%解決你的問題,類似的可以參考攜程/美團(tuán) 酒店列表之類的,為了防止爬蟲未登錄的賬號查詢都不會返回價(jià)格。
這個(gè)正好前兩天遇到這個(gè)問題了,你們api請求有數(shù)字簽名嗎?帶時(shí)間戳那種計(jì)算出來的數(shù)字簽名,用這個(gè)簽名做key,然后利用redis的原子計(jì)數(shù)器,大于1的代表重復(fù)請求的,然后拋出異常之類的處理即可
我百度的使用redis鎖
//防止重復(fù)提交 加redis鎖
$key = '_punch_'.$user['id'].'_'.$data['landmark_pk'];
$rs = RedisLocal::get_redis();
$ttl = 10;
$lock = $rs->set($key, $key, ['nx', 'ex' => $ttl]);
if (!$lock) {
throw new BaseException(['code' => -1, 'msg' => '請不要頻繁提交哦~']);
}
$res = $this->checkPrize($params);
if ($rs->get($key) == $key) {
$rs->del($key);
}
首先要區(qū)分是從用戶發(fā)起的還是從跨站攻擊的;
如果是用戶發(fā)起的,每個(gè)用戶都會攜帶憑證,如JWT、cookie等信息,那么我們可以從業(yè)務(wù)的角度進(jìn)行限制,做一些用戶級別的api防抖或者限流,如:redis設(shè)置一個(gè)防抖閾值,總之就是以用戶+業(yè)務(wù)為顆粒度保存一個(gè)計(jì)數(shù)并限制;
我們還可以基于一種約定,設(shè)置一些request token,比如md5(query string/http body + timestamp + jwt/session id),保證一次請求一次token;當(dāng)然這種約定如果是web端,代碼很容易被找到,在app上面比較有效;這樣的好處是也可以有效防止csrf;
當(dāng)然從服務(wù)端出發(fā),我們還可以基于接口的顆粒度設(shè)置一些限流服務(wù),比如使用基于Redis-lua的限流插件;
我的建議是服務(wù)端最好把以上三種都實(shí)現(xiàn),同時(shí)客戶端做好防抖。
公共接口的話,使用一些人機(jī)驗(yàn)證組件即可。