Procházet zdrojové kódy

开放接口验证

moonsflyer před 4 měsíci
rodič
revize
ab09a07909

+ 25 - 0
app/openapi/config/route.php

@@ -0,0 +1,25 @@
+<?php
+// +----------------------------------------------------------------------
+// | likeshop100%开源免费商用商城系统
+// +----------------------------------------------------------------------
+// | 欢迎阅读学习系统程序代码,建议反馈是我们前进的动力
+// | 开源版本可自由商用,可去除界面版权logo
+// | 商业版本务必购买商业授权,以免引起法律纠纷
+// | 禁止对系统程序代码以任何目的,任何形式的再发布
+// | gitee下载:https://gitee.com/likeshop_gitee
+// | github下载:https://github.com/likeshop-github
+// | 访问官网:https://www.likeshop.cn
+// | 访问社区:https://home.likeshop.cn
+// | 访问手册:http://doc.likeshop.cn
+// | 微信公众号:likeshop技术社区
+// | likeshop团队 版权所有 拥有最终解释权
+// +----------------------------------------------------------------------
+// | author: likeshopTeam
+// +----------------------------------------------------------------------
+
+return [
+    'middleware' => [
+        app\openapi\http\middleware\EncryptMiddleware::class,      // 加密验证
+        app\openapi\http\middleware\ParamValidateMiddleware::class, // 参数验证
+    ],
+];

+ 62 - 0
app/openapi/controller/BaseOpenController.php

@@ -0,0 +1,62 @@
+<?php
+// +----------------------------------------------------------------------
+// | likeshop100%开源免费商用商城系统
+// +----------------------------------------------------------------------
+// | 欢迎阅读学习系统程序代码,建议反馈是我们前进的动力
+// | 开源版本可自由商用,可去除界面版权logo
+// | 商业版本务必购买商业授权,以免引起法律纠纷
+// | 禁止对系统程序代码以任何目的,任何形式的再发布
+// | gitee下载:https://gitee.com/likeshop_gitee
+// | github下载:https://github.com/likeshop-github
+// | 访问官网:https://www.likeshop.cn
+// | 访问社区:https://home.likeshop.cn
+// | 访问手册:http://doc.likeshop.cn
+// | 微信公众号:likeshop技术社区
+// | likeshop团队 版权所有 拥有最终解释权
+// +----------------------------------------------------------------------
+// | author: likeshopTeam
+// +----------------------------------------------------------------------
+
+namespace app\openapi\controller;
+
+use app\common\controller\BaseLikeShopController;
+
+/**
+ * 对外接口基础控制器
+ * Class BaseOpenController
+ * @package app\openapi\controller
+ */
+class BaseOpenController extends BaseLikeShopController
+{
+    protected string $appId = 'rskf371083250903';
+    protected array $appInfo = [];
+
+    /**
+     * 初始化
+     */
+    public function initialize()
+    {
+        if (isset($this->request->appInfo) && $this->request->appInfo) {
+            $this->appInfo = $this->request->appInfo;
+            $this->appId = $this->request->appInfo['app_id'] ?? '';
+        }
+    }
+
+    /**
+     * 获取应用信息
+     * @return array
+     */
+    protected function getAppInfo(): array
+    {
+        return $this->appInfo;
+    }
+
+    /**
+     * 获取应用ID
+     * @return string
+     */
+    protected function getAppId(): string
+    {
+        return $this->appId;
+    }
+}

+ 65 - 0
app/openapi/controller/UserController.php

@@ -0,0 +1,65 @@
+<?php
+// +----------------------------------------------------------------------
+// | likeshop100%开源免费商用商城系统
+// +----------------------------------------------------------------------
+// | 欢迎阅读学习系统程序代码,建议反馈是我们前进的动力
+// | 开源版本可自由商用,可去除界面版权logo
+// | 商业版本务必购买商业授权,以免引起法律纠纷
+// | 禁止对系统程序代码以任何目的,任何形式的再发布
+// | gitee下载:https://gitee.com/likeshop_gitee
+// | github下载:https://github.com/likeshop-github
+// | 访问官网:https://www.likeshop.cn
+// | 访问社区:https://home.likeshop.cn
+// | 访问手册:http://doc.likeshop.cn
+// | 微信公众号:likeshop技术社区
+// | likeshop团队 版权所有 拥有最终解释权
+// +----------------------------------------------------------------------
+// | author: likeshopTeam
+// +----------------------------------------------------------------------
+
+namespace app\openapi\controller;
+
+use app\openapi\validate\UserValidate;
+
+/**
+ * 用户接口示例
+ * Class UserController
+ * @package app\openapi\controller
+ */
+class UserController extends BaseOpenController
+{
+    /**
+     * 获取用户信息
+     * @return \think\response\Json
+     */
+    public function info()
+    {
+
+        $params = (new UserValidate())->post()->goCheckStrict('info');
+
+        // 模拟用户数据
+        $userData = [
+            'user_id' => $params['user_id'],
+            'nickname' => '测试用户',
+            'avatar' => 'https://example.com/avatar.jpg',
+            'mobile' => '138****8888',
+            'created_time' => date('Y-m-d H:i:s'),
+        ];
+        
+        return $this->success('获取成功', $userData);
+    }
+    
+    /**
+     * 更新用户信息
+     * @return \think\response\Json
+     */
+    public function update()
+    {
+        $params = (new UserValidate())->post()->goCheckStrict('update');
+        
+        // 这里处理更新逻辑
+        // ...
+        
+        return $this->success('更新成功');
+    }
+}

+ 88 - 0
app/openapi/http/middleware/EncryptMiddleware.php

@@ -0,0 +1,88 @@
+<?php
+// +----------------------------------------------------------------------
+// | likeshop100%开源免费商用商城系统
+// +----------------------------------------------------------------------
+// | 欢迎阅读学习系统程序代码,建议反馈是我们前进的动力
+// | 开源版本可自由商用,可去除界面版权logo
+// | 商业版本务必购买商业授权,以免引起法律纠纷
+// | 禁止对系统程序代码以任何目的,任何形式的再发布
+// | gitee下载:https://gitee.com/likeshop_gitee
+// | github下载:https://github.com/likeshop-github
+// | 访问官网:https://www.likeshop.cn
+// | 访问社区:https://home.likeshop.cn
+// | 访问手册:http://doc.likeshop.cn
+// | 微信公众号:likeshop技术社区
+// | likeshop团队 版权所有 拥有最终解释权
+// +----------------------------------------------------------------------
+// | author: likeshopTeam
+// +----------------------------------------------------------------------
+
+namespace app\openapi\http\middleware;
+
+use app\common\service\JsonService;
+use app\openapi\service\OpenApiService;
+
+/**
+ * 加密验证中间件
+ * Class EncryptMiddleware
+ * @package app\openapi\http\middleware
+ */
+class EncryptMiddleware
+{
+    /**
+     * 加密验证处理
+     * @param $request
+     * @param \Closure $next
+     * @return mixed
+     */
+    public function handle($request, \Closure $next)
+    {
+        // 获取请求头中的认证信息
+        $appId = $request->header('AppId');
+        $timestamp = $request->header('Timestamp');
+        $nonce = $request->header('Nonce');
+        $signature = $request->header('Signature');
+
+        // 验证必要参数
+        if (empty($appId) || empty($timestamp) || empty($nonce) || empty($signature)) {
+            return JsonService::fail('缺少必要的认证参数', [], 401);
+        }
+
+//        // 验证时间戳(防重放攻击,5分钟内有效)
+//        if (abs(time() - $timestamp) > 300) {
+//            return JsonService::fail('请求时间戳无效', [], 401);
+//        }
+
+        // 验证应用信息
+        $appInfo = OpenApiService::getAppInfo($appId);
+
+        if (empty($appInfo)) {
+            return JsonService::fail('无效的应用ID', [], 401);
+        }
+
+        if ($appInfo['status'] != 1) {
+            return JsonService::fail('应用已被禁用', [], 401);
+        }
+
+        // 验证签名
+        $params = $request->param();
+
+        outFileLog($params,'sign','$params');
+        outFileLog($appInfo['app_secret'],'sign','app_secret');
+        outFileLog($timestamp,'sign','$timestamp');
+        outFileLog($nonce,'sign','$nonce');
+        if (!OpenApiService::verifySignature($params, $appInfo['app_secret'], $timestamp, $nonce, $signature)) {
+            return JsonService::fail('签名验证失败', [], 401);
+        }
+
+//        // 验证随机数(防重复攻击)
+//        if (!OpenApiService::verifyNonce($appId, $nonce, $timestamp)) {
+//            return JsonService::fail('重复的请求', [], 401);
+//        }
+
+        // 将应用信息传递给控制器
+        $request->appInfo = $appInfo;
+
+        return $next($request);
+    }
+}

+ 100 - 0
app/openapi/http/middleware/ParamValidateMiddleware.php

@@ -0,0 +1,100 @@
+<?php
+// +----------------------------------------------------------------------
+// | likeshop100%开源免费商用商城系统
+// +----------------------------------------------------------------------
+// | 欢迎阅读学习系统程序代码,建议反馈是我们前进的动力
+// | 开源版本可自由商用,可去除界面版权logo
+// | 商业版本务必购买商业授权,以免引起法律纠纷
+// | 禁止对系统程序代码以任何目的,任何形式的再发布
+// | gitee下载:https://gitee.com/likeshop_gitee
+// | github下载:https://github.com/likeshop-github
+// | 访问官网:https://www.likeshop.cn
+// | 访问社区:https://home.likeshop.cn
+// | 访问手册:http://doc.likeshop.cn
+// | 微信公众号:likeshop技术社区
+// | likeshop团队 版权所有 拥有最终解释权
+// +----------------------------------------------------------------------
+// | author: likeshopTeam
+// +----------------------------------------------------------------------
+
+namespace app\openapi\http\middleware;
+
+use app\common\service\JsonService;
+use think\facade\App;
+
+/**
+ * 参数验证中间件 - 防止多传参数
+ * Class ParamValidateMiddleware
+ * @package app\openapi\http\middleware
+ */
+class ParamValidateMiddleware
+{
+    /**
+     * 参数验证处理
+     * @param $request
+     * @param \Closure $next
+     * @return mixed
+     */
+    public function handle($request, \Closure $next)
+    {
+        // 获取控制器和方法
+        $controller = $request->controller();
+        $action = $request->action();
+        
+        // 构建验证器类名
+        $validateClass = str_replace('.', '\\', App::getNamespace() . '\\openapi\\validate\\' . $controller . 'Validate');
+        
+        // 检查验证器是否存在
+        if (class_exists($validateClass)) {
+            try {
+                $validate = new $validateClass();
+                
+                // 获取当前请求的所有参数
+                $params = $request->param();
+                
+                // 获取验证器中定义的允许参数
+                $allowedParams = $this->getAllowedParams($validate, $action);
+                
+                if (!empty($allowedParams)) {
+                    // 检查是否有多余参数
+                    $extraParams = array_diff(array_keys($params), $allowedParams);
+                    
+                    if (!empty($extraParams)) {
+                        return JsonService::fail('不允许的参数: ' . implode(', ', $extraParams), [], 400);
+                    }
+                }
+            } catch (\Exception $e) {
+                // 验证器实例化失败,继续执行
+            }
+        }
+        
+        return $next($request);
+    }
+    
+    /**
+     * 获取验证器中允许的参数
+     * @param $validate
+     * @param string $scene
+     * @return array
+     */
+    private function getAllowedParams($validate, string $scene = ''): array
+    {
+        $allowedParams = [];
+        
+        // 获取验证规则
+        if (property_exists($validate, 'rule')) {
+            $rules = $validate->rule ?? [];
+            $allowedParams = array_keys($rules);
+        }
+        
+        // 如果有场景验证,获取场景对应的参数
+        if (!empty($scene) && property_exists($validate, 'scene')) {
+            $scenes = $validate->scene ?? [];
+            if (isset($scenes[$scene])) {
+                $allowedParams = $scenes[$scene];
+            }
+        }
+        
+        return $allowedParams;
+    }
+}

+ 132 - 0
app/openapi/service/OpenApiService.php

@@ -0,0 +1,132 @@
+<?php
+// +----------------------------------------------------------------------
+// | likeshop100%开源免费商用商城系统
+// +----------------------------------------------------------------------
+// | 欢迎阅读学习系统程序代码,建议反馈是我们前进的动力
+// | 开源版本可自由商用,可去除界面版权logo
+// | 商业版本务必购买商业授权,以免引起法律纠纷
+// | 禁止对系统程序代码以任何目的,任何形式的再发布
+// | gitee下载:https://gitee.com/likeshop_gitee
+// | github下载:https://github.com/likeshop-github
+// | 访问官网:https://www.likeshop.cn
+// | 访问社区:https://home.likeshop.cn
+// | 访问手册:http://doc.likeshop.cn
+// | 微信公众号:likeshop技术社区
+// | likeshop团队 版权所有 拥有最终解释权
+// +----------------------------------------------------------------------
+// | author: likeshopTeam
+// +----------------------------------------------------------------------
+
+namespace app\openapi\service;
+
+use think\facade\Cache;
+
+/**
+ * OpenAPI服务类
+ * Class OpenApiService
+ * @package app\openapi\service
+ */
+class OpenApiService
+{
+    /**
+     * 获取应用信息
+     * @param string $appId
+     * @return array|null
+     */
+    public static function getAppInfo(string $appId): ?array
+    {
+        // 这里可以从数据库或配置文件中获取应用信息
+        // 示例数据,实际应用中应该从数据库获取
+        $apps = [
+            '371083' => [
+                'app_id' => 'rsgxsyt371083',
+                'app_name' => '农发商城',
+                'app_secret' => 'T3t6wnKJ6CBn7eaH',
+                'status' => 1, // 1:启用 0:禁用
+                'created_time' => time(),
+            ]
+        ];
+        
+        return $apps[$appId] ?? null;
+    }
+    
+    /**
+     * 验证签名
+     * @param array $params 请求参数
+     * @param string $appSecret 应用密钥
+     * @param int $timestamp 时间戳
+     * @param string $nonce 随机数
+     * @param string $signature 签名
+     * @return bool
+     */
+    public static function verifySignature(array $params, string $appSecret, int $timestamp, string $nonce, string $signature): bool
+    {
+        // 构建签名字符串
+        $signString = self::buildSignString($params, $appSecret, $timestamp, $nonce);
+        
+        // 计算签名
+        $expectedSignature = hash('sha256', $signString);
+        outFileLog($expectedSignature,'sign','$expectedSignature');
+        return hash_equals($expectedSignature, $signature);
+    }
+    
+    /**
+     * 构建签名字符串
+     * @param array $params
+     * @param string $appSecret
+     * @param int $timestamp
+     * @param string $nonce
+     * @return string
+     */
+    private static function buildSignString(array $params, string $appSecret, int $timestamp, string $nonce): string
+    {
+
+        // 移除签名相关参数
+        unset($params['signature']);
+
+        // 参数排序
+        ksort($params);
+
+        // 构建查询字符串
+        $queryString = http_build_query($params);
+
+        // 构建签名字符串: 参数字符串 + 时间戳 + 随机数 + 密钥
+        return $queryString . $timestamp . $nonce . $appSecret;
+    }
+    
+    /**
+     * 验证随机数(防重放攻击)
+     * @param string $appId
+     * @param string $nonce
+     * @param int $timestamp
+     * @return bool
+     */
+    public static function verifyNonce(string $appId, string $nonce, int $timestamp): bool
+    {
+        $cacheKey = 'openapi_nonce_' . $appId . '_' . $nonce;
+        
+        // 检查随机数是否已使用
+        if (Cache::has($cacheKey)) {
+            return false;
+        }
+        
+        // 缓存随机数,过期时间为10分钟
+        Cache::set($cacheKey, $timestamp, 600);
+        
+        return true;
+    }
+    
+    /**
+     * 生成签名(供客户端使用)
+     * @param array $params
+     * @param string $appSecret
+     * @param int $timestamp
+     * @param string $nonce
+     * @return string
+     */
+    public static function generateSignature(array $params, string $appSecret, int $timestamp, string $nonce): string
+    {
+        $signString = self::buildSignString($params, $appSecret, $timestamp, $nonce);
+        return hash('sha256', $signString);
+    }
+}

+ 91 - 0
app/openapi/validate/BaseOpenValidate.php

@@ -0,0 +1,91 @@
+<?php
+// +----------------------------------------------------------------------
+// | likeshop100%开源免费商用商城系统
+// +----------------------------------------------------------------------
+// | 欢迎阅读学习系统程序代码,建议反馈是我们前进的动力
+// | 开源版本可自由商用,可去除界面版权logo
+// | 商业版本务必购买商业授权,以免引起法律纠纷
+// | 禁止对系统程序代码以任何目的,任何形式的再发布
+// | gitee下载:https://gitee.com/likeshop_gitee
+// | github下载:https://github.com/likeshop-github
+// | 访问官网:https://www.likeshop.cn
+// | 访问社区:https://home.likeshop.cn
+// | 访问手册:http://doc.likeshop.cn
+// | 微信公众号:likeshop技术社区
+// | likeshop团队 版权所有 拥有最终解释权
+// +----------------------------------------------------------------------
+// | author: likeshopTeam
+// +----------------------------------------------------------------------
+
+namespace app\openapi\validate;
+
+use app\common\validate\BaseValidate;
+use app\common\service\JsonService;
+
+/**
+ * 对外接口验证基类
+ * Class BaseOpenValidate
+ * @package app\openapi\validate
+ */
+class BaseOpenValidate extends BaseValidate
+{
+    /**
+     * 严格参数验证 - 不允许多传参数
+     * @param null $scene
+     * @param array $validate_data
+     * @return array
+     */
+    public function goCheckStrict($scene = null, $validate_data = [])
+    {
+
+        $method = $this->request->method();
+        // 接收参数
+        if ($method == 'GET') {
+            $params = request()->get();
+        } else {
+            $params = request()->post();
+        }
+
+        // 合并验证参数
+        $params = array_merge($params, $validate_data);
+        
+        // 获取允许的参数列表
+        $allowedParams = $this->getAllowedParams($scene);
+
+        // 检查多余参数
+        if (!empty($allowedParams)) {
+            $extraParams = array_diff(array_keys($params), $allowedParams);
+            if (!empty($extraParams)) {
+                JsonService::throw('不允许的参数: ' . implode(', ', $extraParams));
+            }
+        }
+
+        // 执行常规验证
+        return $this->goCheck($scene, $params);
+    }
+    
+    /**
+     * 获取允许的参数列表
+     * @param string|null $scene
+     * @return array
+     */
+    protected function getAllowedParams($scene = null): array
+    {
+        $allowedParams = [];
+        
+        // 获取验证规则中的参数
+        if (property_exists($this, 'rule')) {
+            $allowedParams = array_keys($this->rule);
+        }
+        
+        // 如果有场景验证,获取场景对应的参数
+        if (!empty($scene) && property_exists($this, 'scene')) {
+            $scenes = $this->scene ?? [];
+            if (isset($scenes[$scene])) {
+                $allowedParams = $scenes[$scene];
+            }
+        }
+        
+        return $allowedParams;
+    }
+}

+ 49 - 0
app/openapi/validate/UserValidate.php

@@ -0,0 +1,49 @@
+<?php
+// +----------------------------------------------------------------------
+// | likeshop100%开源免费商用商城系统
+// +----------------------------------------------------------------------
+// | 欢迎阅读学习系统程序代码,建议反馈是我们前进的动力
+// | 开源版本可自由商用,可去除界面版权logo
+// | 商业版本务必购买商业授权,以免引起法律纠纷
+// | 禁止对系统程序代码以任何目的,任何形式的再发布
+// | gitee下载:https://gitee.com/likeshop_gitee
+// | github下载:https://github.com/likeshop-github
+// | 访问官网:https://www.likeshop.cn
+// | 访问社区:https://home.likeshop.cn
+// | 访问手册:http://doc.likeshop.cn
+// | 微信公众号:likeshop技术社区
+// | likeshop团队 版权所有 拥有最终解释权
+// +----------------------------------------------------------------------
+// | author: likeshopTeam
+// +----------------------------------------------------------------------
+
+namespace app\openapi\validate;
+
+/**
+ * 用户验证器
+ * Class UserValidate
+ * @package app\openapi\validate
+ */
+class UserValidate extends BaseOpenValidate
+{
+    protected $rule = [
+        'user_id' => 'require|integer|gt:0',
+        'nickname' => 'max:50',
+        'avatar' => 'url',
+        'mobile' => 'mobile',
+    ];
+    
+    protected $message = [
+        'user_id.require' => '用户ID不能为空',
+        'user_id.integer' => '用户ID必须为整数',
+        'user_id.gt' => '用户ID必须大于0',
+        'nickname.max' => '昵称不能超过50个字符',
+        'avatar.url' => '头像必须是有效的URL',
+        'mobile.mobile' => '手机号格式不正确',
+    ];
+    
+    protected $scene = [
+        'info' => ['user_id'],
+        'update' => ['user_id', 'nickname', 'avatar', 'mobile'],
+    ];
+}