一个严谨的接口调用

入口

use Apis\Factory;
use finger\Log;

class IndexController extends \Common\controllers\Api
{
    /**
     * API入口。
     * 普通 POST 方式提交。
     */
    public function indexAction()
    {
        // [1]
        define('IS_API', true);
        header("Access-Control-Allow-Origin: *");
        header('Content-type: application/json');
        // [2]
        $params = [
            'post'  => $this->_request->getPost(),
            'input' => file_get_contents('php://input')
        ];
        // [3]
        $apiObj = Factory::factory($params);
        $result = $apiObj->getResult();

        // [4] 记录响应日志。
        Log::writeApiResponseLog($result);
        // [5] 输出结果。
        echo json_encode($result, JSON_UNESCAPED_UNICODE);
        // [6]
        $this->end();
    }
}

工厂验证

 /**
     * 根据接口名称返回接口对象。
     * 
     * -- 1、接口名称转类名称规则:user.login = UserLoginApi
     * -- 2、当 method 参数为空的时候,要抛出异常给调用的人捕获处理。
     *
     * @param  array  $apiData 请求来的所有参数。
     * @throws Exception
     * @return Api
     */
    public static function factory(&$apiData)
    {
        // [1]
        $reqParams = DecodeAdapter::parse($apiData);
        self::writeRequestLog($apiData, $reqParams);
        // [2]
        if (!isset($reqParams['method']) || strlen($reqParams['method']) === 0) {
            Core::exception(STATUS_METHOD_NOT_EXISTS, 'method does not exist');
        }
        if (!isset($reqParams['v']) || strlen($reqParams['v']) === 0) {
            Core::exception(STATUS_VERSION_NOT_EXISTS, 'version number is wrong');
        }
        if (!isset($reqParams['appid']) || strlen($reqParams['appid']) === 0) {
            Core::exception(STATUS_APPID_NOT_EXISTS, 'appid parameters cannot be empty');
        }
        if (!isset($reqParams['timestamp']) || strlen($reqParams['timestamp']) === 0) {
            Core::exception(STATUS_TIMESTAMP_NOT_EXISTS, 'timestamp parameters cannot be empty');
        }

        // [3] 将 method 参数转换为实际的接口类名称。
        $apiName   = $reqParams['method'];
        $params    = explode('.', $apiName);
        $classname = '';
        foreach ($params as $param) {
            $classname .= ucfirst($param);
        }
        // 1.0.0 => v1_0_0
        $version = str_replace('.', '', $reqParams['v']);

        // [4]
        $apiDir = self::apiDir($reqParams['method']);
        if (strlen($apiDir) > 0) {
            $apiDir = "{$apiDir}\\";
        }

        // [5] 读取 appid 配置信息。
        $apiDetail = self::getApiDetail($reqParams['appid']);

        // [6] IP 限制判断。
        $ip   = Ip::ip();
        $bool = ApiAuth::checkIpAllowAccess($apiDetail, $ip);
        if ($bool == false) {
            Core::exception(STATUS_IP_FORBID, '受限 IP 不允许访问');
        }

        // [7] 映射接口类。
        $classname = "\\Apis\\{$apiDetail['api_type']}\\v{$version}\\{$apiDir}{$classname}Api";
        if (strlen($apiName) && class_exists($classname)) {
            return new $classname($reqParams, $apiDetail['api_type'], $apiDetail['api_key'], $apiDetail['api_secret']);
        } else {
            Core::exception(STATUS_API_NOT_EXISTS, '您的 APP 太旧请升级!');
        }
    }

类验证

/**
     * 构造方法。
     *
     * @param  array   $data       所有请求过来的参数。
     * @param  string  $apiType    API 接口类型。
     * @param  string  $apiKey     接口标识。
     * @param  string  $apiSecret  接口密钥。  
     *
     * -- 1、合并提交的参数。
     * -- 2、调用权限判断。
     * -- 3、签名验证。
     * -- 4、参数格式判断。
     * -- 5、运行接口逻辑。
     */
    public function __construct(&$data, $apiType, $apiKey = '', $apiSecret = '')
    {
        $this->apiType   = $apiType;
        $this->timestamp = $_SERVER['REQUEST_TIME'];
        $this->params    = $data;
        $this->apiKey    = $apiKey;
        $this->apiSecret = $apiSecret;

        $this->checkIpAccessPermission();

        $this->checkTimeLag();

        $this->checksignature();

        $this->runService();
    }

签名验证

/**
     * 验证码请求签名。
     * 
     * @param  array  $params     请求参数。
     * @param  string $apiSecret  API 密钥。
     *
     * @return bool
     */
    public static function checkSign($params, $apiSecret)
    {
        $sign = $params['sign'];
        unset($params['sign']);
        ksort($params);
        $str = '';
        foreach ($params as $key => $value) {
            if (!is_array($value) && strlen($value) != 0) {
                $str .= "{$key}{$value}"; // 非数组的值才能进行签名。
            }
        }
        $str    = $str . $apiSecret;
        $okSign = strtoupper(md5($str));
        if (App::getConfig('app.env') != ENV_DEV) {
            if (strlen($sign) === 0 || $sign != $okSign) {
                Core::exception(STATUS_SERVER_ERROR, 'API signature error');
            }
        }
    }

各种验证,太可怕了。

posted @ 2019-12-27 20:11  TBHacker  阅读(275)  评论(0编辑  收藏  举报