Главная
Блог разработчиков phpBB
 
+ 17 предустановленных модов
+ SEO-оптимизация форума
+ авторизация через соц. сети
+ защита от спама

Притворствуем официальным приложением ВКонтакте

Anna | 29.05.2014 | нет комментариев
Началось всё с того, как мой друг попросил меня опубликовать на его странице от его имени пост с моего iPad’а. Дальше я… Не знаю, как это описать… А потом задумался о том, как же формальные приложения ВК для мобильных устройств и планшетов исполняют какие-либо действия.
Вначале подумал, что приложение отправляет через POST либо GET во ВКонтактик какие-то данные. Потом осознал, что, скорее каждого, приложение авторизуется через API. Зашёл на страницу «Разработчикам» в ВК, предпочел «Standalone/Mobile-приложения», предпочел «OAuth-авторизация». Увидел, как приложение должно авторизовываться. Оно должно создавать окно с диалогом авторизации, с такой ссылкой:
oauth.vk.com/authorize?client_id=APP_ID&scope=PERMISSIONS&redirect_uri=REDIRECT_URI&display=DISPLAY&v=API_VERSION&response_type=token
И так, вначале необходимо узнать id официального приложения.
Я решил начать с приложения для iOS, а именно для iPad.
Запостил на своей стене запись с него, открыл на десктопе. Навёл мышку на значок Яблока, и увидел желаемое «vk.com/app3682744». С этим, самым простым, мы совладали. Права я решил установить только доступ к стене (и, механически, к стержневой информации), если что-то нужно будет ещё добавить, это дозволено будет сделать потом. И, безусловно, необходимо включить помимо «wall» ещё и «offline» — доступ к API в всякое время со стороннего сервера. Напротив, делать что-либо сумеет только сервер, куда приложение отсылает все действия, и через тот, что всё делается (допустимо данный сервер — сервер ВК, но я решил не выяснять, так как это мне не необходимо. Сервер прописывается в настройках приложения). И так, теперь наша ссылка выглядит как oauth.vk.com/authorize?client_id=3682744&scope=wall,offline&redirect_uri=REDIRECT_URI&display=DISPLAY&v=API_VERSION&response_type=token
С redirect_uri разберёмся потом, как с самым трудным. «display» — внешний окна авторизации, page, popup либо mobile. Предпочтем page.
«v» выставим последнюю, 5.7. response_type изменять не необходимо, нам получить токен и нужно.
Сейчас будем думать над «redirect_uri». Нам необходима страница, которая покажет токен. И здесь я примечаю «Если Вы разрабатываете браузерное Javascript-приложение…». Понимаю, что именно это нам подходит, так как мы обращаемся со «стороннего сервера». Там написано, что в таком случае нужно указать «oauth.vk.com/blank.html». Так и сделаем. Сейчас наша ссылка выглядит так: oauth.vk.com/authorize?client_id=3682744&v=5.7&scope=wall,offline&redirect_uri=http://oauth.vk.com/blank.html&display=page&response_type=token
Переходим по ссылке, и получаем то, что мы и хотели:
image
Нажимаем на «позволить».
image
Получаем предупреждение, и ссылку в адресной строке в видеoauth.vk.com/blank.html#access_token=ТОКЕН&expires_in=0&user_id=IDПОЛЬЗОВАТЕЛЯ, ТОЕСТЬНАС
Ура! Мы получили, что хотели. А дальше дозволено экспериментировать с функциями. Испробуем применять «wall.post», функцию для создания записи на стене. Позже маленького раздумья понимаем, что нужно перейти по ссылке «api.vk.com/method/wall.post?owner_id=ТУТ_ID_ПОЛЬЗОВАТЕЛЯ&message=ТУТ_ТЕКСТ&v=5.0&access_token=ТОКЕН». Проверяем, убеждаемся, что работает. Радуемся.
Позже поиска среди записей, нахожу id приложения для WindowsPhone — 3502561. Ещё чуть-чуть радуюсь. Решаю для ещё какого-то числа лулзов сделать приложения ВУтюг и ВОкно. Их id — 3998121 и 4147789. Ссылка всюду верно такая же, только параметр «client_id» нужно поменять на id приложения.

Для полной чистки совести решаю написать ещё и от имени Android и iPhone, нахожу их ID (28909846 и 3087106), и тут… Взамен страницы разрешения система выдаёт грозное “{«error»:«invalid_access»,«error_description»:«Security issue»}”. Я начинаю думать, в чём дело. Через какое-то время припоминаю, что приложения для Android и WindowsPhone авторизуются через OAuth. Захожу в документацию. Вижу, что для того, Дабы авторизоваться через OAuth, необходим ключ приложения. Его у меня, внятное дело, нет. Решаюсь на отчаянный шаг — нахожу в интернете официальное приложение ВКонтакте для Android’а, и декомпилирую его. Позже приблизительно двухчасовых рысканий по исходникам понимаю, что обнаружить ключ не реально. Начинаю придумывать иной метод. Наконец мне это докучает, и я начинаю заниматься другими делами. И здесь до меня доходит!.. чай в первых версиях API была только прямая авторизация, а это значит, что ветхие версии приложений не работают через OAuth, а, так как ВКонтакте не могло оставить не работающими ветхие версии, должна быть какая-то уловка. И здесь у меня возникает феноменальная идея (всё феноменальное — легко): в ссылке для приобретения токена, скажем, вoauth.vk.com/authorize?client_id=3682744&v=5.7&scope=wall,offline&redirect_uri=http://oauth.vk.com/blank.html&display=page&response_type=token указывается версия API системы, скажем здесь — последняя, 5.7. А если указать ветхую, скажем, 3.0? А если вообще не указывать версию? Проверяю на приложении для Android: перехожу по ссылкеoauth.vk.com/authorize?client_id=2890984&scope=notify,friends,photos,audio,video,docs,notes,pages,status,offers,questions,wall,groups,messages,notifications,stats,ads,offline&redirect_uri=http://api.vk.com/blank.html&display=page&response_type=token (на каждый случай даю полные права) и… у меня получается! ВКонтакте спрашивает разрешение, я нажимаю на «позволить» и оно мне выдаёт токен!

Позже этого, для теста, пишу в адресной строке api.vk.com/method/wall.post?owner_id=ТУТ_мой_ID&message=Ура! Я сумел написать от имени Android!&v=5.0&access_token=тут_мой_токен. Запись на моей стене возникает.
Позже этого я решаю для своего комфорта сделать типичную прогу для работы с API ВКонтакте. Пишу я её на php:

<?
class Vk{

    const CALLBACK_BLANK = 'https://oauth.vk.com/blank.html';
    const AUTHORIZE_URL = 'https://oauth.vk.com/authorize?client_id={client_id}&scope={scope}&redirect_uri={redirect_uri}&display={display}&v=5.0&response_type={response_type}';
    const GET_TOKEN_URL = 'https://oauth.vk.com/access_token?client_id={client_id}&client_secret={client_secret}&code={code}&redirect_uri={redirect_uri}';
    const METHOD_URL = 'https://api.vk.com/method/';

    public $secret_key = null;
    public $scope = array();
    public $client_id = null;
    public $access_token = null;
    public $owner_id = 0;

    function __construct($options = array()){

        $this->scope[]='offline';

        if(count($options) > 0){
            foreach($options as $key => $value){
                if($key == 'scope' && is_string($value)){
                    $_scope = explode(',', $value);
                    $this->scope = array_merge($this->scope, $_scope);
                } else {
                    $this->$key = $value;
                }

            }
        }
    }

    /**
     * Выполнение вызова Api способа
     * @param string $method - способ, http://vk.com/dev/methods
     * @param array $vars - параметры способа
     * @return array - выводит массив данных либо ошибку (но тоже в массиве)
     */
    function api($method = '', $vars = array()){

        $params = http_build_query($vars);

        $url = $this->http_build_query($method, $params);

        return (array)$this->call($url);
    }

    /**
     * Построение финального URI для выхова
     * @param $method
     * @param string $params
     * @return string
     */
    private function http_build_query($method, $params = ''){
        return  self::METHOD_URL . $method . '?' . $params.'&access_token=' . $this->access_token;
    }

    /**
     * Получить ссылка на запрос прав доступа
     *
     * @param string $type тип результата (code - одноразовый код авторизации , token - готовый access token)
     * @return mixed
     */
    public function get_code_token($type="code"){

        $url = self::AUTHORIZE_URL;

        $scope = implode(',', $this->scope);

        $url = str_replace('{client_id}', $this->client_id, $url);
        $url = str_replace('{scope}', $scope, $url);
        $url = str_replace('{redirect_uri}', self::CALLBACK_BLANK, $url);
        $url = str_replace('{display}', 'page', $url);
        $url = str_replace('{response_type}', $type, $url);

        return $url;

    }

    public function get_token($code){

        $url = self::GET_TOKEN_URL;
        $url = str_replace('{code}', $code, $url);
        $url = str_replace('{client_id}', $this->client_id, $url);
        $url = str_replace('{client_secret}', $this->secret_key, $url);
        $url = str_replace('{redirect_uri}', self::CALLBACK_BLANK, $url);

        return $this->call($url);
    }

    function call($url = ''){

        if(function_exists('curl_init')) $json = $this->curl_post($url); else $json = file_get_contents($url);

        $json = json_decode($json, true);

        if(isset($json['response'])) return $json['response'];

        return $json;
    }

    // @deprecated
    private function curl_get($url)
    {
        if(!function_exists('curl_init')) return false;

        $ch = curl_init($url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        $tmp = curl_exec ($ch);
        curl_close ($ch);
        $tmp = preg_replace('/(?s)<meta http-equiv="Expires"[^>]*>/i', '', $tmp);
        return $tmp;
    }

    private function curl_post($url){

        if(!function_exists('curl_init')) return false;

        $param = parse_url($url);

        if( $curl = curl_init() ) {

            curl_setopt($curl, CURLOPT_URL, $param['scheme'].'://'.$param['host'].$param['path']);
            curl_setopt($curl, CURLOPT_RETURNTRANSFER,true);
            curl_setopt($curl, CURLOPT_POST, true);
            curl_setopt($curl, CURLOPT_POSTFIELDS, $param['query']);
            $out = curl_exec($curl);

            curl_close($curl);

            return $out;
        }

        return false;
    }
    /**
     * @param array $options
     */
    public function set_options($options = array()){

        if(count($options) > 0){
            foreach($options as $key => $value){
                if($key == 'scope' && is_string($value)){
                    $_scope = explode(',', $value);
                    $this->scope = array_merge($this->scope, $_scope);
                } else {
                    $this->$key = $value;
                }

            }
        }

    }

    /**
     * @param bool $gid
     * @param array $files
     * @return array|bool
     */
    function upload_photo($gid = false, $files = array()){

        if(count($files) == 0) return false;
        if(!function_exists('curl_init')) return false;

        $data_json = $this->api('photos.getWallUploadServer', array('gid'=> intval($gid)));

        if(!isset($data_json['upload_url'])) return false;

        $temp = array_chunk($files, 4);

        $files = array();
        $attachments = array();

        foreach ($temp[0] as $key => $data) {
            $files['file' . ($key 1)] = '@' . $data;
        }

        $upload_url = $data_json['upload_url'];

        $ch = curl_init($upload_url);
        curl_setopt($ch, CURLOPT_HTTPHEADER, array("Content-type: multipart/form-data"));
        curl_setopt($ch, CURLOPT_HEADER, 0);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($ch, CURLOPT_USERAGENT, "Mozilla/4.0 (compatible;)");
        curl_setopt($ch, CURLOPT_POSTFIELDS, $files);

        $upload_data = json_decode(curl_exec($ch), true);

        $response = $this->api('photos.saveWallPhoto', $upload_data);

        if(count($response) > 0){

            foreach($response as $photo){

                $attachments[] = $photo['id'];
            }
        }

        return $attachments;

    }

    /**
     * Заливка документа (скажем GIF файл)
     *
     * @param bool $gid
     * @param $file
     * @return bool|string
     */
    function upload_doc($gid = false, $file){

        if(!is_string($file)) return false;
        if(!function_exists('curl_init')) return false;

        $data_json = $this->api('docs.getUploadServer', array('gid'=> intval($gid)));

        var_dump($data_json);

        if(!isset($data_json['upload_url'])) return false;

        $attachment = false;

        $files['file'] = '@' . $file;

        $upload_url = $data_json['upload_url'];

        $ch = curl_init($upload_url);
        curl_setopt($ch, CURLOPT_HTTPHEADER, array("Content-type: multipart/form-data"));
        curl_setopt($ch, CURLOPT_HEADER, 0);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($ch, CURLOPT_USERAGENT, "Mozilla/4.0 (compatible;)");
        curl_setopt($ch, CURLOPT_POSTFIELDS, $files);

        $upload_data = json_decode(curl_exec($ch), true);

        $response = $this->api('docs.save', $upload_data);

        if(count($response) > 0){

            foreach($response as $photo){

                $attachment = 'doc'.$photo['owner_id'].'_'.$photo['did'];
            }
        }

        return $attachment;

    }

    /**
     *
     * Заливка видео
     *
     * http://vk.com/dev/video.save
     *
     * @param array $options
     * @param bool $file
     * @return bool|string
     */
    function upload_video($options = array(), $file = false){

        if(!is_array($options)) return false;
        if(!function_exists('curl_init')) return false;

        $data_json = $this->api('video.save', $options);

        if(!isset($data_json['upload_url'])) return false;

        $attachment = 'video'.$data_json['owner_id'].'_'.$data_json['vid'];

        $upload_url = $data_json['upload_url'];
        $ch = curl_init($upload_url);

        curl_setopt($ch, CURLOPT_HTTPHEADER, array("Content-type: multipart/form-data"));
        curl_setopt($ch, CURLOPT_HEADER, 0);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($ch, CURLOPT_USERAGENT, "Mozilla/4.0 (compatible;)");

        // если указан файл то заливаем его отправкой POST переменной video_file
        if($file && file_exists($file)){

            $files['video_file'] = '@' . $file;
            curl_setopt($ch, CURLOPT_POSTFIELDS, $files);
            curl_exec($ch);

        // напротив легко обращаемся по адресу (ну нужно так!)
        } else {

            curl_exec($ch);
        }

        return $attachment;

    }

}

// сейчас - используем наш класс:
    $config['client_id'] = 2890984; //ID приложения, здесь - Android 
    $config['user_id'] = 3087106; // id нынешнего пользователя (необходимо не во всех случаях)
    $config['access_token'] = '778eee10654727ea96fec257f4fa4b39bd23b403649265d0fe7c37c9b01bf4c25fcb66de3738d670d5bc9'; //токен, тоже необходим не неизменно
    //$config['scope'] = 'wall,photos,video'; // права доступа к способам (для генерации токена)

    $v = new Vk($config);

	$method = 'wall.postt'; ///название способа, тот, что мы используем, в данном случае мы отпраляем запись на стену

	$params = array(); //в этом массиве - параметры, которые мы отправляем
	$params['message'] = 'I love Habrahabr!'; //в данном случае - только само сообщение
    $response = $v->api($method, $params); //выполняем
   print_r($response); //печатаем результат, тот, что приходит в формате JSON, но в виде массива
?>

P.S.: Это мой 1-й пост, следственно умоляю за недочёты не только минусовать, но и выкладывать конструктивную критику в комментариях
Спасибо за внимание!

Источник: programmingmaster.ru

Оставить комментарий
Форум phpBB, русская поддержка форума phpBB
Рейтинг@Mail.ru 2008 - 2017 © BB3x.ru - русская поддержка форума phpBB