使用阿里簡訊(防止薅羊毛、限制傳送次數)直接複製直接用

SanXiao發表於2021-12-06

來展示:

1、首先你得裝阿里簡訊sdk

文件傳送門

composer require alibabacloud/sdk

發簡訊控制器

<?php
namespace App\Controllers;
use App\Requests\App\AppSendSmsRequest;
use App\Services\SmsService as Service;
use Framework\BaseController;
use Framework\Hope;
use Mews\Captcha\Facades\Captcha;
class SmsController extends BaseController
{
    /**
     * @var Service
     */
    private $service;
    public function __construct(Service $service)
    {
        $this->service = $service;
    }
    /** 提交圖形驗證碼
     * @param AppSendSmsRequest $request
     */
    public function send_sms(AppSendSmsRequest $request)
    {
        $phone = $request->phone();
        $key = $request->key();
        $code = $request->code();
        $flg = captcha_api_check($code, $key);
        if (!$flg && (!Hope::isProduct() && $code != 'ASDF')) {
            $this->retError('圖形驗證碼錯誤');
        }
        if(!Hope::isProduct()){
            $code = substr($phone, -4);
            $redis = Hope::getRedis();
            $redis->setex("sms:" . $phone, 60 * 5, $code);
        }else{
            //獲取簡訊驗證碼
            $this->getVerifyCode($phone);
        }
        $this->retSuccess(true);
    }
    /**
     * 獲取圖形驗證碼
     */
    public function get_sms()
    {
        $res = Captcha::create('default', true);
        $this->retSuccess($res);
    }
    /**
     * 獲取簡訊驗證碼
     */
    public function getVerifyCode($phone)
    {
        $this->service->sendSmsVerifyCode($phone);
    }
}

服務層

<?php
namespace App\Services;
use App\Services\Sms\HttpService;
use Carbon\Carbon;
use Framework\BaseService;
use Framework\Hope;
class SmsService extends BaseService
{
    /** 傳送簡訊驗證碼
     * @param string $phone
     * @return bool
     * @throws \AlibabaCloud\Client\Exception\ClientException
     */
    public function sendSmsVerifyCode(string $phone)
    {
        if(!Hope::isProduct()){
            return true;
        }
        $redis = Hope::getRedis();
        $key = $this->getKey($phone);
        $limitKey = 'code_limit';
        // 驗證碼重發限制
        $jsonData = $redis->get($key);
        $data = $jsonData ? json_decode($jsonData, true) : [];
        if ($data && Carbon::now()->timestamp < $data['resend_expire']) {
            $this->retError( '簡訊已在1分鐘內發出,請耐心等');
        }
        // 手機號限制
        $sendCnt = $redis->zScore($limitKey, $phone);
        if ($sendCnt && $sendCnt >= env('ONE_DAY_FREQ', 1)) {
            $this->retError("獲取驗證碼次數已達上限");
        }
        //生成4位驗證碼
        $code = $this->createVerifyCode($phone);
        //傳送簡訊獲取驗證碼
        app(HttpService::class)->sendSms($phone,$code);
        //redis中儲存驗證碼
        $this->saveSmsCode($phone, $code, $key, $limitKey);
    }
    public function saveSmsCode(string $phone, string $code, string $key, string $limitKey)
    {
        $redis = Hope::getRedis();
        $data = [
            'code' => $code,
            'resend_expire' => Carbon::now()->addSeconds(env('RESEND_SEC', 60))->timestamp,
        ];
        $redis->set($key, json_encode($data));
        $redis->expire($key, (int) env('EXPIRE_SEC', 1800)); // 設定驗證碼過期時間
        // 設定手機號限制
        $redis->zIncrBy($limitKey, 1, $phone);
        $redis->expireAt($limitKey, (int) Carbon::today()->addDay()->timestamp);
        if (! Hope::isProduct()) {
            return true;
        }
    }
    /**
     * 驗證簡訊驗證碼
     * @param string $phone 手機號
     * @param string $code  驗證碼
     * @return bool
     */
    public function checkSmsCode(string $phone, string $code): bool
    {
        $testSmsCode = config('constant.sms_code');
        if (env('APP_ENV', 'dev') != 'prod' && $code == $testSmsCode) {
            return true;
        }
        $arr = [$testSmsCode, $this->createVerifyCode($phone)];
        if (!Hope::isProduct() && in_array($code, $arr )) {
            return true;
        }
        $key = $this->getKey($phone);
        $jsonData = Hope::getRedis()->get($key);
        $data = $jsonData ? json_decode($jsonData, true) : [];
        if ($data && $code === $data['code']) {
            return true;
        }
        $this->retError("驗證碼有誤,請重新輸入!");
    }
    /**
     * @param string $phone 手機號
     * @return string
     */
    private function getKey(string $phone): string
    {
        return "code:" . $phone;
    }
    /**
     * 建立4位驗證碼
     * @param string $phone 手機號
     * @return string
     */
    private function createVerifyCode(string $phone): string
    {
        if(!Hope::isProduct()){
            return substr($phone, -4);
        }
        return (string)mt_rand(1000, 9999);
    }
}

順便把公共函式貼出來

<?php
namespace Framework;
use App\Services\UploadService;
use Illuminate\Redis\Connections\PhpRedisConnection;
use Illuminate\Support\Facades\Redis;
use SimpleSoftwareIO\QrCode\BaconQrCodeGenerator;
class Hope
{
    public static function isProduct(): bool
    {
        $env = env('APP_ENV', 'local');
        if ($env === 'prod') {
            return true;
        }
        return false;
    }
    public static function getRedis(string $connection = 'default'): PhpRedisConnection
    {
        /** @var PhpRedisConnection $redis */
        $redis = Redis::connection($connection);
        return $redis;
    }
    public static function getLoginId(): int
    {
        $jwt = app(Jwt::class);
        $user_id = $jwt->checkToken();
        if ($user_id === false) {
            return 0;
        }
        return $user_id;
    }
    /**
     * 獲取隨機邀請碼
     * @return string
     */
    public static function makeInviterCode() {
        $code = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
        $rand = $code[rand(0,25)]
            .strtoupper(dechex(date('m')))
            .date('d').substr(time(),-5)
            .substr(microtime(),2,5)
            .sprintf('%02d',rand(0,99));
        for(
            $a = md5( $rand, true ),
            $s = '0123456789ABCDEFGHIJKLMNOPQRSTUV',
            $d = '',
            $f = 0;
            $f < 8;
            $g = ord( $a[ $f ] ),
            $d .= $s[ ( $g ^ ord( $a[ $f + 8 ] ) ) - $g & 0x1F ],
            $f++
        );
        return $d;
    }
    /**
     * 建立二維碼
     * @param $inviterCode
     * @return string
     */
    public static function makeInviterQrcode($inviterCode)
    {
        $text = $_SERVER["HTTP_HOST"] . '/app.apk';
        $size = 200;
        //$img = public_path('/logo.png');
        $qr   = new BaconQrCodeGenerator();
        $img  = $qr->format('png')->size($size)->margin(1)->generate($text);
        $type = 'qrcode';
        $path = app(UploadService::class)->uploadQiniu($img,$type,$inviterCode);
        return $path;
    }
    /**
     * @param string $prefix
     * @return string
     */
    public static function random_order($prefix='MC')
    {
        return $prefix . date('YmdHis', time()) . substr(microtime(), 2, 6) . sprintf('%03d', rand(0, 999));
    }
    public static function name_auth($name,$id_card){
        $host = "https://idcert.market.alicloudapi.com";
        $path = "/idcard";
        $method = "GET";
        $appcode = "111111111111111111111";//開通服務後 買家中心-檢視AppCode
        $headers = array();
        array_push($headers, "Authorization:APPCODE " . $appcode);
        $querys = "idCard={$id_card}&name=".urlencode($name);
        $bodys = "";
        $url = $host . $path . "?" . $querys;
        $curl = curl_init();
        curl_setopt($curl, CURLOPT_CUSTOMREQUEST, $method);
        curl_setopt($curl, CURLOPT_URL, $url);
        curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
        curl_setopt($curl, CURLOPT_FAILONERROR, false);
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($curl, CURLOPT_HEADER, true);
        if (1 == strpos("$" . $host, "https://")) {
            curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
            curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false);
        }
        $out_put = curl_exec($curl);
        $httpCode = curl_getinfo($curl, CURLINFO_HTTP_CODE);
        list($header, $body) = explode("\r\n\r\n", $out_put, 2);
        if ($httpCode == 200) {
            return $body;
        }
    }
}

最後簡訊基類

<?php
namespace App\Services\Sms;
use AlibabaCloud\Client\AlibabaCloud;
use AlibabaCloud\Client\Exception\ClientException;
use AlibabaCloud\Client\Exception\ServerException;
use Framework\BaseService;
use Framework\Hope;
class HttpService extends BaseService
{
    /** 阿里簡訊配置
     * @param string $phone
     * @param string $code
     * @return bool
     * @throws ClientException
     */
    public function sendSms(string $phone, string $code)
    {
        if(!Hope::isProduct()){
            return true;
        }
        $accessKey = env('ALIYUN_SMS_ACCESS_KEY');
        $secretKey = env('ALIYUN_SMS_ACCESS_SECRET');
        $query = [
            'PhoneNumbers'  => $phone,
            'SignName'      => env('ALIYUN_SMS_SIGN_NAME'),
            'TemplateCode'  => env('ALIYUN_SMS_SEND_CODE'),
            'TemplateParam' => json_encode(['code' => $code]),
        ];
        AlibabaCloud::accessKeyClient($accessKey, $secretKey)
            ->regionId('cn-hangzhou')
            ->asDefaultClient();
        try {
            $result = AlibabaCloud::rpc()
                ->product('Dysmsapi')
                ->scheme('https')
                ->version('2017-05-25')
                ->action('SendSms')
                ->method('POST')
                ->host('dysmsapi.aliyuncs.com')
                ->options([
                    'query' => $query
                ])
                ->request();
        } catch (ClientException $e) {
            $this->retError("簡訊傳送失敗");
        } catch (ServerException $e) {
            $this->retError("簡訊傳送失敗");
        }
        $result = $result->toArray();
        if ($result['Code'] == 'OK') {
            return true;
        } else {
            $this->retError("簡訊傳送失敗");
        }
    }
}

最後在推薦一個類也很好用哦,有興趣可以去了解一下
overtrue/easy-sms

本作品採用《CC 協議》,轉載必須註明作者和本文連結
過去心不可得,現在心不可得,未來心不可得

相關文章