shish 1 сар өмнө
parent
commit
b5daa86582

+ 78 - 5
app-ghs/controllers/AuthController.php

@@ -816,40 +816,113 @@ class AuthController extends PublicController
         $postParams = Yii::$app->request->post();
         $allParams = array_merge($getParams, $postParams);
         $mobile = $allParams['mobile'] ?? '';
+        $captchaVerifyParam = $allParams['captchaVerifyParam'] ?? '';
 
         if (!stringUtil::isMobile($mobile)) {
             util::fail('请填写正确手机号');
         }
 
-        // 1. 安全检查:单手机号 60 秒防刷
+        // 1. 验证阿里云人机验证码(生产环境强制,本地环境若未传则容灾放行)
+        if (empty($captchaVerifyParam)) {
+            if (Yii::getAlias('@env') !== 'local') {
+                util::fail('请先完成人机验证');
+            }
+        } else {
+            if (!$this->verifyAliyunCaptcha($captchaVerifyParam)) {
+                util::fail('人机验证未通过,请重试');
+            }
+        }
+
+        // 2. 安全检查:单手机号 60 秒防刷
         $lockKey = 'LOGIN_SMS_LOCK_' . $mobile;
         if (Yii::$app->redis->executeCommand('GET', [$lockKey])) {
             util::fail('发送过于频繁,请稍后再试');
         }
 
-        // 2. 安全检查:单手机号每日上限 5 条
+        // 3. 安全检查:单手机号每日上限 5 条
         $dayKey = 'LOGIN_SMS_COUNT_' . date('Ymd') . '_' . $mobile;
         $sendCount = (int)Yii::$app->redis->executeCommand('GET', [$dayKey]);
         if ($sendCount >= 5) {
             util::fail('该手机号今日获取验证码已达上限');
         }
 
-        // 3. 生成 6 位随机验证码并保存至 Redis(有效期 5 分钟)
+        // 4. 生成 6 位随机验证码并保存至 Redis(有效期 5 分钟)
         $code = (string)rand(100000, 999999);
         $codeCacheKey = 'LOGIN_SMS_CODE_' . $mobile;
         Yii::$app->redis->executeCommand('SETEX', [$codeCacheKey, 300, $code]);
 
-        // 4. 调用系统自带的限流发送组件 (内置了 IP 60秒锁和每日15条限制)
+        // 5. 调用系统自带的限流发送组件 (内置了 IP 60秒锁和每日15条限制)
         $minute = 5;
         sms::send($mobile . ',' . $code . ',' . $minute, '登录验证码:{$var},{$var}分钟内有效');
 
-        // 5. 写入单手机号限制锁
+        // 6. 写入单手机号限制锁
         Yii::$app->redis->executeCommand('SETEX', [$lockKey, 60, '1']);
         Yii::$app->redis->executeCommand('SETEX', [$dayKey, 86400, $sendCount + 1]);
 
         util::complete('验证码发送成功');
     }
 
+    /**
+     * 【用途】校验阿里云验证码2.0
+     * 【为什么】利用已安装的 alibabacloud/client 进行通用 RPC 调用,校验前端传来的验证参数
+     */
+    private function verifyAliyunCaptcha($captchaVerifyParam)
+    {
+        if (empty($captchaVerifyParam)) {
+            return false;
+        }
+
+        try {
+            $accessKeyId = Yii::$app->params['aliyun']['accessKeyId'] ?? Yii::$app->params['accessKeyId'] ?? '';
+            $accessKeySecret = Yii::$app->params['aliyun']['accessKeySecret'] ?? Yii::$app->params['accessKeySecret'] ?? '';
+            $sceneId = Yii::$app->params['aliyun']['captchaSceneId'] ?? Yii::$app->params['aliyunCaptchaSceneId'] ?? 'YOUR_SCENE_ID';
+
+            if (empty($accessKeyId) || empty($accessKeySecret)) {
+                Yii::error("阿里云验证码校验失败:未配置 AccessKey");
+                return false;
+            }
+
+            // 初始化阿里云客户端
+            \AlibabaCloud\Client\AlibabaCloud::accessKeyClient($accessKeyId, $accessKeySecret)
+                ->regionId('cn-hangzhou')
+                ->asDefaultClient();
+
+            // 发起通用 RPC 请求调用 VerifyIntelligentCaptcha
+            $result = \AlibabaCloud\Client\AlibabaCloud::rpc()
+                ->product('Captcha')
+                ->version('2023-03-05')
+                ->action('VerifyIntelligentCaptcha')
+                ->method('POST')
+                ->scheme('https')
+                ->options([
+                    'query' => [
+                        'CaptchaVerifyParam' => $captchaVerifyParam,
+                        'SceneId' => $sceneId,
+                    ],
+                ])
+                ->request();
+
+            $data = $result->toArray();
+            
+            Yii::info("阿里云验证码返回结果: " . json_encode($data), 'captcha');
+
+            // 兼容可能包裹在 Result 节点下或扁平结构
+            $verifyResult = $data['Result']['VerifyResult'] ?? $data['VerifyResult'] ?? null;
+            if ($verifyResult === true) {
+                return true;
+            }
+
+            return false;
+        } catch (\Exception $e) {
+            Yii::error("阿里云验证码校验异常: " . $e->getMessage());
+            // 容灾策略:如果是本地环境,可以放行
+            if (Yii::getAlias('@env') === 'local') {
+                return true;
+            }
+            return false;
+        }
+    }
+
     /**
      * 【用途】短信验证码登录
      * 【为什么】提供免密码快捷登录方式,验证通过后自动签发 JWT Token 并返回商家后台所需的所有初始化数据