company_banner

WeChat. Сериализуем объект — получаем СМС

    Предыдущая статья.
    В продолжение темы WeChat. В данной статье мы покажем как сериализуются и десериализуются объекты, а так же зашифруем сообщение и получим СМС с кодом подтверждения. Кроме того мы приведем весь необходимый код на PHP, чтобы вы могли попробовать и убедиться что все работает



    В приложении используется библиотека ProtoBuf.

    Существует базовый объект WXPBGeneratedMessage, все остальные наследуются от него.
    Изначально Protobuf умеет сериализовать только простые типы(строки и числа). Чтобы сериализовать сложные объекты, для каждого объекта создается поле, которое мы назвали classInfo. В нем хранятся имена и описания всех полей.

    Для запроса СМС необходимо создать объект BindOpMobileRequest.

    public function __construct()
        {
            $this->classInfo = new \wechat\PBClassInfo();
    
            $this->classInfo->nameProperty[] = 'baseRequest';
            $this->classInfo->objectDefinition[] = new \wechat\ObjectDefinition(0x1, 0x2, 0xB, 0x0, 0x0, 'BaseRequest');
    
            $this->classInfo->nameProperty[] = 'userName';
            $this->classInfo->objectDefinition[] = new \wechat\ObjectDefinition(0x2, 0x1, 0x9, 0x0, 0x0, '');
    
            $this->classInfo->nameProperty[] = 'mobile';
            $this->classInfo->objectDefinition[] = new \wechat\ObjectDefinition(0x3, 0x1, 0x9, 0x0, 0x0, '');
    
            $this->classInfo->nameProperty[] = 'opcode';
            $this->classInfo->objectDefinition[] = new \wechat\ObjectDefinition(0x4, 0x2, 0x5, 0x0, 0x0, '');
    
            $this->classInfo->nameProperty[] = 'verifycode';
            $this->classInfo->objectDefinition[] = new \wechat\ObjectDefinition(0x5, 0x1, 0x9, 0x0, 0x0, '');
    
            $this->classInfo->nameProperty[] = 'dialFlag';
            $this->classInfo->objectDefinition[] = new \wechat\ObjectDefinition(0x6, 0x1, 0x5, 0x0, 0x0, '');
    
            $this->classInfo->nameProperty[] = 'dialLang';
            $this->classInfo->objectDefinition[] = new \wechat\ObjectDefinition(0x7, 0x1, 0x9, 0x0, 0x0, '');
    
            $this->classInfo->nameProperty[] = 'authTicket';
            $this->classInfo->objectDefinition[] = new \wechat\ObjectDefinition(0x8, 0x1, 0x9, 0x0, 0x0, '');
    
            $this->classInfo->nameProperty[] = 'forceReg';
            $this->classInfo->objectDefinition[] = new \wechat\ObjectDefinition(0x9, 0x1, 0xD, 0x0, 0x0, '');
    
            $this->classInfo->nameProperty[] = 'safeDeviceName';
            $this->classInfo->objectDefinition[] = new \wechat\ObjectDefinition(0xA, 0x1, 0x9, 0x0, 0x0, '');
    
            $this->classInfo->nameProperty[] = 'safeDeviceType';
            $this->classInfo->objectDefinition[] = new \wechat\ObjectDefinition(0xB, 0x1, 0x9, 0x0, 0x0, '');
    
            $this->classInfo->nameProperty[] = 'randomEncryKey';
            $this->classInfo->objectDefinition[] = new \wechat\ObjectDefinition(0xC, 0x1, 0xB, 0x0, 0x0, 'SKBuiltinBuffer_t');
    
            $this->classInfo->nameProperty[] = 'language';
            $this->classInfo->objectDefinition[] = new \wechat\ObjectDefinition(0xD, 0x1, 0x9, 0x0, 0x0, '');
    
            $this->classInfo->nameProperty[] = 'inputMobileRetrys';
            $this->classInfo->objectDefinition[] = new \wechat\ObjectDefinition(0xE, 0x1, 0xD, 0x0, 0x0, '');
    
            $this->classInfo->nameProperty[] = 'adjustRet';
            $this->classInfo->objectDefinition[] = new \wechat\ObjectDefinition(0xF, 0x1, 0xD, 0x0, 0x0, '');
    
            $this->classInfo->nameProperty[] = 'clientSeqId';
            $this->classInfo->objectDefinition[] = new \wechat\ObjectDefinition(0x10, 0x1, 0x9, 0x0, 0x0, '');
    
            parent::__construct();
        }

    Как можно увидеть, classInfo состоит из двух массивов(имена полей и их описания). ObjectDefinition состоит из пяти чисел и строки:

    1. Порядковый номер поля
    2. Идентификатор массива. Ставится равным трем, если поле является массивом. В остальных случаях ни на что не влияет
    3. Тип поля (например 0x9 — string, 0x8 — bool, 0x7 — unsigned int, 0xB — объект итд )
    4. Используется при сериализации массивов вместе со вторым пунктом, в остальных случаях равен нулю
    5. Назначение этого числа осталось загадкой (оно всегда равно нулю и нигде не применяется)
    6. В случае если поле является объектом, указывается тип объекта, в противном случае указывается пустая строка.

    Объект сериализуется рекурсивно по алгоритму прямого обхода дерева в глубину. Перед каждым полем сначала ставиться тег, который вычисляется (c использованием словаря) следующим образом:

    
    $FieldTypeDictionary = array(-1, 1, 5, 0, 0, 0, 1, 5, 0, 2, 3, 2, 2, 0, 0, 5, 1, 0, 0);
    $fieldType = $FieldTypeDictionary[$fieldType];
    $tag = $fieldNumber << 3 | $fieldType;

    Если поле является объектом, то после тега ставиться его длинна.

    Готовим сообщение для отправки на сервер


    Создаем объект BindOpMobileRequest, заполняем поля и сериализуем:

     public function getVerifyCode()
        {
            $bindOpMobileRequest = new \wechat\Request\BindOpMobileRequest();
            $baseRequest = $this->createBaseRequest();
            $this->aesKey = random_bytes(0x10);
            $baseRequest->sessionKey = '';
            $baseRequest->scene = 0;
            $bindOpMobileRequest->baseRequest = $baseRequest;
            $bindOpMobileRequest->mobile = $this->phoneNumber;
            $bindOpMobileRequest->opcode = 14;
            $bindOpMobileRequest->safeDeviceName = $this->deviceName;
            $bindOpMobileRequest->safeDeviceType = 'iPhone';
            $bindOpMobileRequest->randomEncryKey = new \wechat\Object\SKBuiltinBuffer_t();
            $bindOpMobileRequest->randomEncryKey->iLen = strlen($this->aesKey);
            $bindOpMobileRequest->randomEncryKey->buffer = $this->aesKey;
            $bindOpMobileRequest->language = $this->language;
            $bindOpMobileRequest->inputMobileRetrys = 5;
            $bindOpMobileRequest->adjustRet = 0;
            $bindOpMobileRequest->clientSeqId = $this->clientSeqId;
    
            $serializedData = $bindOpMobileRequest->serializedData();
    
        }
    

    В результате получаем:



    Теперь осталось, составить заголовок, зашифровать данные и отправить на сервер.

    $header = $this->computeHeader($serializedData, 0x91, 2);
    $dataToSend = $header . $this->client->RSAEncrypt($serializedData);
    $response = $this->client->request($this, $dataToSend, 'bindopmobileforreg');
    

    Если все сделано правильно, сервер пришлет нам ответ, который нужно расшифровать AES-ключом переданным в запросе и десериализовать:

    $response = deleteHeaderFromResponse($response);
    $response = $this->client->AESDecrypt($response, $this->aesKey);
    $bindOpMobileResponse = new \wechat\Response\BindOpMobileResponse();
    $bindOpMobileResponse->mergeFromData($response);

    Десериализовать необходимо в объект BindOpMobileResponse.
    Здесь приведен код на php, реализующий все выше сказанное. Дерзайте…

    PS. SMS не приходят на номера одной «жужжащей» компании. Не понятно почему…
    Бринго 36,99
    Компания
    Поделиться публикацией
    Похожие публикации
    Комментарии 0

    Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

    Самое читаемое