Przeglądaj źródła

Merge branch 'dev' into dev-sxb

sunxbiao 1 rok temu
rodzic
commit
0e14883bd7

+ 2 - 1
.gitignore

@@ -2,4 +2,5 @@ public/
2 2
 .idea/
3 3
 .env
4 4
 runtime/
5
-nohup.out
5
+nohup.out
6
+*.pem

+ 60 - 0
app/common/middleware/RateLimitMiddleware.php

@@ -0,0 +1,60 @@
1
+<?php
2
+
3
+namespace app\common\middleware;
4
+
5
+use think\facade\Cache;
6
+use think\Response;
7
+
8
+class RateLimitMiddleware
9
+{
10
+    // 频率限制配置(单位:秒)
11
+    protected $rules = [
12
+        'ip' => ['limit' => 10, 'window' => 60],    // 每IP每分钟10次
13
+        'mobile' => ['limit' => 5, 'window' => 3600] // 每手机号每小时5次
14
+    ];
15
+
16
+    public function handle($request, \Closure $next)
17
+    {
18
+        $mobile = $request->param('mobile');
19
+        $ip = $request->ip();
20
+
21
+        // IP频率检查
22
+        $ipKey = "rate:ip:{$ip}";
23
+        if (!$this->checkRate($ipKey, $this->rules['ip'])) {
24
+            return $this->tooManyRequests('IP请求过于频繁');
25
+        }
26
+
27
+        // 手机号频率检查
28
+        $mobileKey = "rate:mobile:{$mobile}";
29
+        if (!$this->checkRate($mobileKey, $this->rules['mobile'])) {
30
+            return $this->tooManyRequests('该手机号请求过于频繁');
31
+        }
32
+
33
+        return $next($request);
34
+    }
35
+
36
+    protected function checkRate(string $key, array $rule): bool
37
+    {
38
+        $count = Cache::get($key, 0);
39
+
40
+        if ($count >= $rule['limit']) {
41
+            return false;
42
+        }
43
+
44
+        Cache::inc($key);
45
+        if ($count === 0) {
46
+            Cache::expire($key, $rule['window']);
47
+        }
48
+
49
+        return true;
50
+    }
51
+
52
+    protected function tooManyRequests(string $message): Response
53
+    {
54
+        return json([
55
+            'code' => 429,
56
+            'msg' => $message,
57
+            'data' => null
58
+        ])->code(429);
59
+    }
60
+}

+ 98 - 0
app/common/middleware/SignatureMiddleware.php

@@ -0,0 +1,98 @@
1
+<?php
2
+
3
+namespace app\common\middleware;
4
+
5
+use think\facade\Cache;
6
+use think\facade\Log;
7
+use think\Request;
8
+use think\Response;
9
+
10
+class SignatureMiddleware
11
+{
12
+    public function handle(Request $request, \Closure $next)
13
+    {
14
+        // 获取必要参数
15
+        $essential = $request->only(['timestamp', 'nonce', 'signature']);
16
+
17
+        // 验证必要参数
18
+        if (count(array_filter($essential)) !== 3) {
19
+            return $this->errorResponse('参数不完整', 400);
20
+        }
21
+
22
+        // 验证时间戳(5分钟内有效)
23
+        if (abs(time() - (int)$essential['timestamp']) > 300) {
24
+            return $this->errorResponse('请求已过期', 400);
25
+        }
26
+
27
+        // 验证nonce唯一性
28
+        $nonceKey = 'nonce:' . $essential['nonce'];
29
+        if (Cache::has($nonceKey)) {
30
+            return $this->errorResponse('重复请求', 400);
31
+        }
32
+
33
+        // 构造签名数据(与前端完全一致)
34
+        $signData = $request->param();
35
+        unset($signData['signature']);
36
+
37
+        // 按键名排序
38
+        ksort($signData);
39
+
40
+        // 生成签名字符串(与前端相同格式)
41
+        $signContent = '';
42
+        foreach ($signData as $key => $value) {
43
+            $signContent .= "{$key}={$value}&";
44
+        }
45
+        $signContent = rtrim($signContent, '&');
46
+
47
+        // 记录原始签名内容(用于调试)
48
+        Log::debug("Sign Content: " . $signContent);
49
+
50
+        // 获取公钥
51
+        $publicKey = openssl_pkey_get_public(
52
+            file_get_contents(env('RSA_PUBLIC_KEY_PATH'))
53
+        );
54
+
55
+        if (!$publicKey) {
56
+            Log::error("公钥加载失败");
57
+            return $this->errorResponse('系统错误', 500);
58
+        }
59
+
60
+        // 计算签名的MD5值(与前端一致)
61
+        $md5Hash = md5($signContent);
62
+        Log::debug("MD5 Hash: " . $md5Hash);
63
+
64
+        // 解码前端签名(前端使用公钥加密)
65
+        $signature = base64_decode($essential['signature']);
66
+
67
+        // 使用公钥解密签名
68
+        $decrypted = '';
69
+        $success = openssl_public_decrypt($signature, $decrypted, $publicKey);
70
+
71
+        if (!$success) {
72
+            Log::error("签名解密失败: " . openssl_error_string());
73
+            return $this->errorResponse('签名验证失败', 403);
74
+        }
75
+
76
+        Log::debug("解密结果: " . $decrypted);
77
+
78
+        // 比较解密后的值与MD5哈希
79
+        if ($decrypted !== $md5Hash) {
80
+            Log::error("签名验证失败: 期望 {$md5Hash}, 实际 {$decrypted}");
81
+            return $this->errorResponse('签名验证失败', 403);
82
+        }
83
+
84
+        // 记录已使用的nonce(5分钟过期)
85
+        Cache::set($nonceKey, 1, 300);
86
+
87
+        return $next($request);
88
+    }
89
+
90
+    private function errorResponse(string $message, int $code): Response
91
+    {
92
+        return json([
93
+            'code' => $code,
94
+            'msg' => $message,
95
+            'data' => null
96
+        ])->code($code);
97
+    }
98
+}

+ 1 - 0
app/common/repositories/user/UserRepository.php

@@ -1085,6 +1085,7 @@ class UserRepository extends BaseRepository
1085 1085
                     'commission_type' => $commission_type,
1086 1086
                     'mark' => empty($mark) ?'管理员调整收益':$mark ,
1087 1087
                     'create_time' => date('Y-m-d H:i:s'),
1088
+                    'take_time' => time()
1088 1089
                 ];
1089 1090
                 if($commission_type != 5){
1090 1091
                     $isert_data['balance'] = $user['brokerage_price'];

+ 59 - 20
app/controller/api/Auth.php

@@ -48,12 +48,14 @@ use think\App;
48 48
 use think\db\exception\DataNotFoundException;
49 49
 use think\db\exception\DbException;
50 50
 use think\db\exception\ModelNotFoundException;
51
+use think\Env;
51 52
 use think\Exception;
52 53
 use think\exception\HttpResponseException;
53 54
 use think\facade\Cache;
54 55
 use think\facade\Db;
55 56
 use think\facade\Log;
56 57
 use think\Filesystem;
58
+use think\Response;
57 59
 
58 60
 /**
59 61
  * Class Auth
@@ -1322,6 +1324,41 @@ class Auth extends BaseController
1322 1324
         return $res;
1323 1325
     }
1324 1326
 
1327
+
1328
+    /**
1329
+     * 获取验签公钥
1330
+     * @return Response
1331
+     */
1332
+    public function getPublicKey(): Response
1333
+    {
1334
+        $path = env('RSA_PUBLIC_KEY_PATH');
1335
+
1336
+        if (empty($path)) {
1337
+            return json([
1338
+                'code' => 500,
1339
+                'msg' => '公钥路径未配置',
1340
+                'data' => null
1341
+            ]);
1342
+        }
1343
+
1344
+        if (!file_exists($path)) {
1345
+            return json([
1346
+                'code' => 500,
1347
+                'msg' => '公钥文件不存在: ' . $path,
1348
+                'data' => null
1349
+            ]);
1350
+        }
1351
+
1352
+        $publicKey = file_get_contents($path);
1353
+        return json([
1354
+            'code' => 200,
1355
+            'msg' => 'success',
1356
+            'data' => [
1357
+                'public_key' => $publicKey
1358
+            ]
1359
+        ]);
1360
+    }
1361
+
1325 1362
     public function verify(UserAuthValidate $validate)
1326 1363
     {
1327 1364
         $data = $this->request->params(['phone', 'code', 'key',"type"]);
@@ -1464,30 +1501,32 @@ class Auth extends BaseController
1464 1501
 
1465 1502
         $domain = $this->request->domain();
1466 1503
 
1467
-        if (!$this->checkSmsCode($data['phone'], $data['sms_code'])) {
1468
-            if (($data['sms_code'] != '6865')) {
1469
-                $uid= Db::name('user')->where("phone", $data["phone"])->value('uid');
1470
-                if($uid){
1471
-                    $allTeams = Db::name('user_zero_line')->field('team_uid,custom_code')->select()->toArray();
1472
-                    $hasTeam = false;
1473
-                    foreach ($allTeams as $team) {
1474
-                        $sql = "select getUserLevelId({$team['team_uid']}) as uids";
1475
-                        $spread_user_list = Db::query($sql);
1476
-                        $member_uids = explode(",", trim($spread_user_list[0]['uids'], '$,'));
1477
-                        if (in_array($uid, $member_uids)){
1478
-                            $hasTeam = true;
1479
-                            if ($data['sms_code'] != $team['custom_code']) {
1480
-                                return app('json')->fail('验证码不正确');
1481
-                            }
1482
-                            break;
1504
+        //如果手机验证码错误并且验证码不是6865,进入查验团队验证码环节
1505
+        if (!$this->checkSmsCode($data['phone'], $data['sms_code']) && $data['sms_code'] !== '6865') {
1506
+            $uid= Db::name('user')->where("phone", $data["phone"])->value('uid');//通过输入的手机号查询用户的uid
1507
+            if($uid){
1508
+                $allTeams = Db::name('user_zero_line')->field('team_uid,custom_code')->select()->toArray();//去user_zero_line表查询团队team_uid和团队自定义的验证码
1509
+                $hasTeam = false;  //先将用户设成不为团队成员
1510
+                foreach ($allTeams as $team) {
1511
+                    $cacheKey = 'team_members_' . $team['team_uid'];
1512
+                    $member_uids = Cache::get($cacheKey);
1513
+                    $sql = "select getUserLevelId({$team['team_uid']}) as uids";  //查询所有团队下成员的uid
1514
+                    $spread_user_list = Db::query($sql);
1515
+                    $member_uids = explode(",", trim($spread_user_list[0]['uids'], '$,'));
1516
+                    Cache::set($cacheKey, $member_uids, 300);  //结果缓存5分钟
1517
+                    if (in_array($uid, $member_uids)){
1518
+                        $hasTeam = true;   //若用户属于团队,将其设为团队成员
1519
+                        if ($data['sms_code'] != $team['custom_code']) {
1520
+                            return app('json')->fail('验证码不正确');  //如果与用户所属团队的验证码不一致,返回验证码不正确
1483 1521
                         }
1522
+                        break;
1484 1523
                     }
1485
-                    if (!$hasTeam) {
1486
-                        return app('json')->fail('验证码不正确');
1487
-                    }
1488
-                } else {
1524
+                }
1525
+                if (!$hasTeam) {
1489 1526
                     return app('json')->fail('验证码不正确');
1490 1527
                 }
1528
+                } else {
1529
+                return app('json')->fail('验证码不正确');
1491 1530
             }
1492 1531
         }
1493 1532
 

+ 1 - 1
app/controller/api/Notify.php

@@ -506,7 +506,7 @@ class Notify
506 506
 
507 507
 
508 508
                                         //Db::name('user')->where('uid',$spread_data['uid'])->update(['contribute'=> $gx_jf,'score'=> $gx_jf,'brokerage_price'=> $yj_amount,'fugou'=>$fgj_amount]);
509
-                                        $this->con_log($spread_data['uid'],round($pv * 0.3,2),$store_order_data['order_id'],0,"推广会员专区商品赠送贡献值",1);
509
+                                        $this->con_log($spread_data['uid'],round($gx_jf * 0.3,2),$store_order_data['order_id'],0,"推广会员专区商品赠送贡献值",1);
510 510
                                         $this->score_log($spread_data['uid'],bcmul($gx_jf, $rate, 2),$store_order_data['order_id'],0,"推广会员专区商品赠送积分",1);
511 511
                                         //推广佣金
512 512
                                         $this->bill_user_log($spread_data['uid'],$yj_amount_90,$store_order_data['order_id'],3,$store_order_data['order_sn'],'推广获得佣金',$store_order_data['mer_id']);

Plik diff jest za duży
+ 1 - 1
extend/BaiduAi/sdk/lib/3e511fab3f565237233736f759f2e3e2


+ 11 - 1
route/api.php

@@ -6,6 +6,8 @@
6 6
  * Copyright (c) http://crmeb.net
7 7
  */
8 8
 
9
+use app\common\middleware\RateLimitMiddleware;
10
+use app\common\middleware\SignatureMiddleware;
9 11
 use think\facade\Route;
10 12
 Route::miss(function () {
11 13
     $DB = DIRECTORY_SEPARATOR;
@@ -970,8 +972,16 @@ Route::group('api/', function () {
970 972
     Route::get('auth/wechat', 'api.Auth/auth');
971 973
     //小程序授权
972 974
     Route::post('auth/mp', 'api.Auth/mpAuth');
975
+
976
+    // 获取验签公钥
977
+    // openssl genrsa -out request_private_key.pem 2048
978
+    // openssl rsa -in request_private_key.pem -pubout -out request_public_key.pem
979
+    Route::get('auth/getPublicKey', 'api.Auth/getPublicKey');
980
+
973 981
     //验证码
974
-    Route::post('auth/verify', 'api.Auth/verify');
982
+    Route::post('auth/verify', 'api.Auth/verify')
983
+        ->middleware(SignatureMiddleware::class)
984
+        ->middleware(RateLimitMiddleware::class);
975 985
     //微信配置
976 986
     Route::get('wechat/config', 'api.Wechat/jsConfig');
977 987
     //图片验证码