首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 相關知識

PHP 實作 IMEI 限制的簡訊驗證碼發送類:防刷、頻率控制與 Redis 儲存

     編輯:PHP 相關知識

簡訊驗證碼發送服務:IMEI 綁定、頻率限制、每日額度控制

完整的驗證碼發送類需解決:防暴力刷碼、同一裝置/手機號限制、驗證碼有效期管理。本文展示基於 Redis 的生產級實作。

核心業務規則

  • 驗證碼有效期:30 分鐘(EXPIRE_SEC = 1800)
  • 重發間隔:60 秒(RESEND_SEC = 60)
  • 單手機號每日上限:5 次(ONE_DAY_FREQ = 5)
  • 單手機號每日不同 IMEI 上限:3 個(ONE_DAY_IMEI_COUNT = 3)

完整類別實作

<?php
class Api_Sms {
    const EXPIRE_SEC = 1800;
    const RESEND_SEC = 60;
    const ONE_DAY_FREQ = 5;
    const ONE_DAY_IMEI_COUNT = 3;
    public $error = [];

    public function sendVerifyCode($mobile, $imei) {
        if (!$this->isMobile($mobile)) {
            $this->error = ['code' => -1, 'msg' => '手機號格式錯誤'];
            return false;
        }
        $redis = Api_Common::redis();
        $vcKey = "VC_{$mobile}";
        $limitKey = "VC_LIMIT_{$mobile}";

        // 1. 重發頻率檢查
        $data = json_decode($redis->get($vcKey), true);
        if ($data && time() < $data['resend_expire']) {
            $this->error = ['code' => -1, 'msg' => '短信已在1分鐘內發出,請耐心等待'];
            return false;
        }

        // 2. IMEI 與手機號限制檢查
        $sendCnt = $redis->zScore($limitKey, $imei);
        if ($sendCnt && $sendCnt >= self::ONE_DAY_FREQ) {
            $this->error = ['code' => -1, 'msg' => '今日發送次數已達上限'];
            return false;
        }
        $imeiCnt = $redis->zCard($limitKey);
        if ($imeiCnt >= self::ONE_DAY_IMEI_COUNT && !$sendCnt) {
            $this->error = ['code' => -1, 'msg' => '已超過驗證碼發送設備限制'];
            return false;
        }

        // 3. 產生/取得驗證碼
        if (!$data) {
            $vc = strval(rand(100000, 999999));
            $data = ['vc' => $vc, 'resend_expire' => 0];
            $redis->set($vcKey, json_encode($data));
            $redis->expire($vcKey, self::EXPIRE_SEC);
        }
        $vc = $data['vc'];

        // 4. 發送簡訊(需對接實際簡訊商 API)
        $result = $this->send($mobile, "安全驗證碼:{$vc}");
        if ($result) {
            // 更新重發時間戳
            $data['resend_expire'] = time() + self::RESEND_SEC;
            $ttl = $redis->ttl($vcKey);
            $redis->set($vcKey, json_encode($data));
            $redis->expire($vcKey, $ttl);
            // 記錄發送統計
            $redis->zIncrBy($limitKey, 1, $imei);
            $redis->expireAt($limitKey, strtotime(date('Y-m-d', strtotime('+1 day'))));
        }
        return $result;
    }

    public function send($mobile, $content) {
        // 對接簡訊商 HTTP API,回傳 bool
        return true; 
    }

    private function isMobile($mobile) {
        return preg_match('/^1\d{10}$/', $mobile);
    }

    public function checkVerifyCode($mobile, $vc) {
        $data = json_decode(Api_Common::redis()->get("VC_{$mobile}"), true);
        return $data && $data['vc'] === $vc;
    }

    public function cleanVerifyCode($mobile) {
        $redis = Api_Common::redis();
        $redis->del("VC_{$mobile}");
        $redis->del("VC_LIMIT_{$mobile}");
    }
}

Redis 資料結構設計

  • VC_{mobile} (String):{vc, resend_expire},TTL 30 分鐘
  • VC_LIMIT_{mobile} (Sorted Set):member=IMEI, score=發送次數,隔日 00:00 過期

擴展建議

  • 加入圖形驗證碼/滑動驗證作為前置關卡
  • 簡訊內容模板化、簽名合規
  • 發送失敗重試機制、失敗記錄告警
  • 支援國際號碼格式驗證
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved