Просмотр исходного кода

Merge remote-tracking branch 'origin/dev' into dev

xupuyuan 1 год назад
Родитель
Сommit
4ae98993a2

+ 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
+}

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

@@ -0,0 +1,57 @@
1
+<?php
2
+
3
+namespace app\common\middleware;
4
+
5
+use think\facade\Cache;
6
+use think\Response;
7
+
8
+class SignatureMiddleware
9
+{
10
+    public function handle($request, \Closure $next)
11
+    {
12
+        // 1. 获取加密签名
13
+        $sign = $request->header('X-Sign');
14
+        if (!$sign) {
15
+            return $this->reject('缺少签名', 401);
16
+        }
17
+
18
+        // 2. 读取私钥
19
+        $privateKey = file_get_contents(env('RSA_PRIVATE_KEY_PATH'));
20
+        if (!$privateKey) {
21
+            return $this->reject('服务器密钥错误', 500);
22
+        }
23
+
24
+        // 3. 解密数据
25
+        $decrypted = '';
26
+        openssl_private_decrypt(base64_decode($sign), $decrypted, $privateKey);
27
+
28
+        if (!$decrypted || !strpos($decrypted, ':')) {
29
+            return $this->reject('无效签名', 403);
30
+        }
31
+
32
+        // 4. 分离随机数和时间戳
33
+        list($nonce, $timestamp) = explode(':', $decrypted, 2);
34
+
35
+        // 5. 验证时间有效性(5分钟内)
36
+        if (abs(time() - $timestamp / 1000) > 300) {
37
+            return $this->reject('请求已过期', 403);
38
+        }
39
+
40
+        // 6. 防重放攻击(检查nonce唯一性)
41
+        $cacheKey = 'nonce_' . $nonce;
42
+        if (Cache::has($cacheKey)) {
43
+            return $this->reject('重复请求', 403);
44
+        }
45
+        Cache::set($cacheKey, 1, 300); // 5分钟缓存
46
+
47
+        return $next($request);
48
+    }
49
+
50
+    private function reject($msg, $code): Response
51
+    {
52
+        return Response::create([
53
+            'code' => $code,
54
+            'msg'  => $msg
55
+        ], 'json')->code($code);
56
+    }
57
+}

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

@@ -1075,6 +1075,7 @@ class UserRepository extends BaseRepository
1075 1075
                     'commission_type' => $commission_type,
1076 1076
                     'mark' => empty($mark) ?'管理员调整收益':$mark ,
1077 1077
                     'create_time' => date('Y-m-d H:i:s'),
1078
+                    'take_time' => time()
1078 1079
                 ];
1079 1080
                 if($commission_type != 5){
1080 1081
                     $isert_data['balance'] = $user['brokerage_price'];

+ 37 - 0
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
+                'publicKey' => $publicKey
1358
+            ]
1359
+        ]);
1360
+    }
1361
+
1325 1362
     public function verify(UserAuthValidate $validate)
1326 1363
     {
1327 1364
         $data = $this->request->params(['phone', 'code', 'key',"type"]);

+ 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']);

Разница между файлами не показана из-за своего большого размера
+ 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;
@@ -969,8 +971,16 @@ Route::group('api/', function () {
969 971
     Route::get('auth/wechat', 'api.Auth/auth');
970 972
     //小程序授权
971 973
     Route::post('auth/mp', 'api.Auth/mpAuth');
974
+
975
+    // 获取验签公钥
976
+    // openssl genrsa -out request_private_key.pem 2048
977
+    // openssl rsa -in request_private_key.pem -pubout -out request_public_key.pem
978
+    Route::get('auth/getPublicKey', 'api.Auth/getPublicKey');
979
+
972 980
     //验证码
973
-    Route::post('auth/verify', 'api.Auth/verify');
981
+    Route::post('auth/verify', 'api.Auth/verify')
982
+        ->middleware(SignatureMiddleware::class)
983
+        ->middleware(RateLimitMiddleware::class);
974 984
     //微信配置
975 985
     Route::get('wechat/config', 'api.Wechat/jsConfig');
976 986
     //图片验证码