Во всех браузерах есть опция, позволяющая запретить приём cookies со сторонних сайтов (например, из iframe с отличным от текущего доменом). В некоторых браузерах (привет, Apple) эта функция включена по умолчанию, но часто пользователи самостоятельно её включают и благополучно об этом забывают, вследствие чего разработчик не может записать необходимые данные в cookies или localstorage. Например, при разработке приложений для ВКонтакте вы можете столкнуться с такой проблемой.
Я хочу поделиться простым и изящным решением, как обойти это ограничение.
В принципе, когда у нас есть любой другой способ идентифицировать пользователя, эта проблема стоит не столь остро, всегда можно по VK ID записать или забрать из базы необходимые данные. Но для небольших приложений это дополнительная нагрузка, тем более, если хранить нужно пару строк.
К счастью, в API ВКонтакте есть методы для хранения данных на серверах ВК (key-value хранилище). Нам остаётся только записать и прочитать оттуда данные. Но мы постараемся сделать это максимально красиво.
Предположим, что нам нужно записать в cookies какие-то пользовательские данные (например, нам надо хранить пользовательский токен авторизации в Instagram). Основная идея — записать данные как в куку, так и в хранилище ВКонтакте, а затем максимально быстро.
В примере выше мы перенаправили пользователя на какую-то страницу, где требуются записанные данные (в нашем приложении это главная страница, которая использует пришедший токен). Понятно, что при следующей загрузке приложения эти данные пропадут из GET–параметра и останутся только в хранилище ВК. Конечно, мы можем достать их с помощью Javascript API, но мы же пишем изящное решение ;-).
Чтобы можно было получить наши данные на серверной стороне приложения мы должны заполнить поле «Первый запрос к API» в настройках приложения:
Таким образом, ВКонтакте при обращении к нашему приложению будет передавать GET–параметр api_result — в нём будет находиться JSON-объект с нашей кукой. Также ничего не мешает проверять наличие куки в nginx (если поменять format на xml):
Так, на серверной стороне мы можем сделать что-то вроде:
и получить данные в целости и сохранности. Если без предложенных извращений с nginx, то достаточно будет разобрать api_result:
P.S. Также, пользуясь случаем предлагаю вашему вниманию интересный способ оптимизации нагрузки с помощью вышеописанного метода. Допустим, в приложении нам нужен некий token, который мы храним в куках или в хранилище ВК. Однако, само приложение — статичный html–файл, в котором серверный язык, например, нужен только для того, чтобы этот токен вставить в приложение. Конечно, мы можем из JS запросить необходимую куку или данные из хранилища ВКонтакте, но мы же пишем изящное решение ;-).
Итак, можно настроить nginx (или Apache) следующим образом:
Ну и в Javascript получить нужные данные:
Таким образом мы получим возможность всех залогиненных пользователей отправлять на html-файл (который nginx отдаст очень быстро), передавая в хэше любые необходиме параметры. А сам файл можно вообще вынести на CDN, тем самым ещё сильнее снизив нагрузку на ваш сервер.
Я хочу поделиться простым и изящным решением, как обойти это ограничение.
В принципе, когда у нас есть любой другой способ идентифицировать пользователя, эта проблема стоит не столь остро, всегда можно по VK ID записать или забрать из базы необходимые данные. Но для небольших приложений это дополнительная нагрузка, тем более, если хранить нужно пару строк.
К счастью, в API ВКонтакте есть методы для хранения данных на серверах ВК (key-value хранилище). Нам остаётся только записать и прочитать оттуда данные. Но мы постараемся сделать это максимально красиво.
Предположим, что нам нужно записать в cookies какие-то пользовательские данные (например, нам надо хранить пользовательский токен авторизации в Instagram). Основная идея — записать данные как в куку, так и в хранилище ВКонтакте, а затем максимально быстро.
var data = 'abcde'; // Наши данные, которые мы обычно храним в куках
document.cookie = "userdata="+data+"; path=/; expires=Tue, 31 Dec 2013 23:59:59 GMT";
// У пользователей с включенной опцией блокирования сторонних кук
// эта кука не установится
// И вот теперь записываем данные в хранилище
VK.api('storage.set', { key: 'userdata', value: data }, function(response) {
// И редиректим на необходимую страницу, добавив в GET-параметр наши данные
window.location = "/page_for_logined_users?userdata="+data;
});
В примере выше мы перенаправили пользователя на какую-то страницу, где требуются записанные данные (в нашем приложении это главная страница, которая использует пришедший токен). Понятно, что при следующей загрузке приложения эти данные пропадут из GET–параметра и останутся только в хранилище ВК. Конечно, мы можем достать их с помощью Javascript API, но мы же пишем изящное решение ;-).
Чтобы можно было получить наши данные на серверной стороне приложения мы должны заполнить поле «Первый запрос к API» в настройках приложения:
method=storage.get&key=userdata&format=json&v=3.0
Таким образом, ВКонтакте при обращении к нашему приложению будет передавать GET–параметр api_result — в нём будет находиться JSON-объект с нашей кукой. Также ничего не мешает проверять наличие куки в nginx (если поменять format на xml):
if ($arg_api_result ~ "%3Cresponse%3E(.*)%3C%2Fresponse%3E") {
set $userdata $1;
rewrite ^ /page_for_logined_users?userdata=$userdata? redirect;
}
Так, на серверной стороне мы можем сделать что-то вроде:
$userdata = (isset($_COOKIE['userdata'])) ? $_COOKIE['userdata'] : (isset($_GET['userdata'])) ? $_GET['userdata'] : false;
и получить данные в целости и сохранности. Если без предложенных извращений с nginx, то достаточно будет разобрать api_result:
if (isset($_GET['api_result'])) {
$data = json_decode($_GET['api_result'], 1);
$userdata = (!empty($data['response'])) ? $data['response'] : false;
}
P.S. Также, пользуясь случаем предлагаю вашему вниманию интересный способ оптимизации нагрузки с помощью вышеописанного метода. Допустим, в приложении нам нужен некий token, который мы храним в куках или в хранилище ВК. Однако, само приложение — статичный html–файл, в котором серверный язык, например, нужен только для того, чтобы этот токен вставить в приложение. Конечно, мы можем из JS запросить необходимую куку или данные из хранилища ВКонтакте, но мы же пишем изящное решение ;-).
Итак, можно настроить nginx (или Apache) следующим образом:
# Если есть кука
if ($http_cookie ~ "userdata") {
set $token $cookie_userdata;
set $gotohtml "1";
}
# Если есть данные из API
if ($arg_api_result ~ "%3Cresponse%3E(.*)%3C%2Fresponse%3E") {
set $token $1;
set $gotohtml "1";
}
# Если не страница авторизации (http://example.com/)
if ($request_uri != /$is_args$args) {
set $gotohtml "";
}
# Идём к статичному кэшируемому файлу
if ($gotohtml ~ 1) {
rewrite ^ /index.html#$token? redirect;
}
Ну и в Javascript получить нужные данные:
var token = window.location.hash.substring(1);
Таким образом мы получим возможность всех залогиненных пользователей отправлять на html-файл (который nginx отдаст очень быстро), передавая в хэше любые необходиме параметры. А сам файл можно вообще вынести на CDN, тем самым ещё сильнее снизив нагрузку на ваш сервер.