- PVSM.RU - https://www.pvsm.ru -
Я буду каждое утро развертывать мир, как резиновую ленту на мяче для гольфа, а вечером завертывать обратно. Если очень попросишь — покажу, как это делается.
Р. Брэдбери
В статье описан Backend-as-a-Service подход к хранению и обработки данных. Рассказаны преимущества и недостатки представителя такого подхода — сервиса parse.com. Коротко представлен сервис аутентификации пользователей через соц. сети uLogin. Основное назначение — показать, как эти два сервиса могут взаимодействовать, чтобы проект не требовал регистрации пользователей по логину и паролю, но в то же время сохранилась возможность авторизации пользователей к действиям над объектами.
Parse.com [1] — один из самых популярных провайдеров backend-as-a-service (BaaS). BaaS подход позволяет не поднимать свой сервер для хранения и обработки данных приложения. Это используется в мобильных разработках и в обычном вебе. Parse.com имеет свои SDK под несколько платформ, в том числе серверных. Но я расскажу о javascript.
Возможность работать с базой данных через javascript, не поднимая свой сервер, открывает отличные возможности, например, для Single page application (SPA), которое можно хостить на Github Pages, Bitbucket и многих других бесплатных. Первый вопрос, который у меня возник, когда я услышал про работу с БД из клиентского кода — это разграничение прав доступа, так как ключи общеизвестны. Изучив документацию parse.com, я выяснил, что для этого используется авторизация пользователей. Каждый пользователь имеет свой логин и пароль. SDK имеет методы регистрации нового пользователя по логину и паролю, аутентификации по этим же данным. Можно добавить email, при этом сам parse.com умеет отправлять настраиваемые письма для верификации email.
var user = new Parse.User();
user.set("username", "my name");
user.set("password", "my pass");
user.set("email", "email@example.com");
// other fields can be set just like with Parse.Object
user.set("phone", "415-392-0202");
user.signUp(null, {
success: function(user) {
// Hooray! Let them use the app now.
},
error: function(user, error) {
// Show the error message somewhere and let the user try again.
alert("Error: " + error.code + " " + error.message);
}
});
Разграничение прав доступа происходит по ACL, которые можно назначать создаваемым объектам. Например, установить для объекта доступ на публичное чтение, но оставив редактирование только авторизованному пользователю. Дополнительно можно устанавливать ограничения на методы по работе с таблицей. Например, можно разрешить всем запись в таблицу, но запретить чтение всем, кроме определенной группы (в случаях логирования, например). Кроме этого, администратору предоставляется мастер-ключ, с помощью которого можно из клиентского приложения получить полный доступ. Но в SPA такое недопустимо, так как ключ не спрятать. Разумеется, есть админка, где можно редактировать схемы, производить CRUD-операции над всеми данными.
Админка:
[2]
Аутентификация пользователя только по логину и паролю сейчас может выглядеть грубо. Поэтому parse.com имеет в своём SDK класс Parse.FacebookUtils, с помощью которого можно свзязать аккаунты пользователя с соц. сетью. Но это единственная социальная сеть, которую можно использовать через SDK parse.com в javascript. Есть Twitter, но не для [3] javascript [4]. Что делать, если очень хочется аутентифицировать пользователей через другие соц. сети?
Parse.com, к счастью, предоставляет не только хранилище данных, но и возможность выполнять некоторый код на их сервере. Фичу они назвали Cloud Code [5]. Код создаваемых функций недоступен для пользователей, а значит это можно использовать при построении подписываемых секретным ключом запросах. Но это не особо-то и пригодилось.
Я начал выбирать сервисы, которые имеют готовые виджеты авторизации пользователей на сайтах, и остановился на uLogin [6]. Пару слов о выборе. Когда-то мне попадался на глаза Логинза, но я остался им не доволен, так как там меня просили вводить логин и пароль даже для входа через Твиттер. Возможно, были какие-то другие, соц. приложения которых просили доступ на написание твитов (omg, бесит). Так вот, uLogin имеет хороший набор соц. сетей, готовый виджет, возврат accessToken в callback-фунции для javascript, а также позволяет создавать свои соц. приложения для аутентификации пользователей.
Так как parse.com просит регистрировать пользователей по логину и паролю, то я пришел к выводу, что эту пару можно получить, используя данные, возвращаемые uLogin. Для этого необходимо создать файл clound/main.js в вашем приложении, в котором нужно определить функцию. Назовём её «getCredentials».
Parse.Cloud.define("getCredentials", function(request, response) {
var token = request.params.token;
var userLogin, userPassword;
response.success({'username': userLogin, 'password': userPassword});
});
Сервис uLogin позволяет настроить [7] список полей, которые он будет возвращать. Но среди обязательных есть:
network – идентификатор соцсети пользователя,
profile – адрес профиля пользователя (ссылка на его страницу в соцсети, если удастся ее получить),
uid – уникальный идентификатор пользователя в рамках соцсети,
identity – глобально уникальный идентификатор пользователя.
identity — это, как правило, тоже url, похожий на profile, поэтому я решил склеивать логин пользователя (здесь username) из network и uid.
Для пароля же нужно что-то более секретное, поэтому я решил в качестве пароля использовать хеш от identity, token, secret.
identity для уникальности, token для секретности, т.к. каждый вход в uLogin будет создавать новый токен, известный только пользователю и самому uLogin, и secret — соль, хранимая в настройках приложения.
Этот подход плох тем, что с каждым новом входом через uLogin, будет новый token, а значит и новый пароль. Поэтому нужно каждый раз сохранять новый пароль пользователя. Однако, parse.com после входа пользователя достаточно долго сохраняет сессию, поэтому аутентифицироваться каждый раз не придется. Готовый Cloud Code [8].
Решение довольно подозрительное по качеству, но в некоторых случаях вполне работоспособно. Например, если отказаться от входа пользователей по их логинам и паролям. Но даже в этом случае, метод можно доработать и перестать обновлять пароль пользователя после получения токена. Как еще один вариант, это использовать в качестве временного пароля случайную строку вместо хеша (осознание этого пришло чуть позже).
Разумеется, временный пароль выдаётся после подтверждения токена, путём получения данных из uLogin. После этого мы определяем, есть ли у нас пользователь с таким логином. Если нет, то регистрируем. Записываем ему данные из соц. сети (псевдоним, аватар) в хранилище, возвращаем пару логин и пароль.
Взаимодействие с uLogin и c parse.com происходит по https, все данные доступны только самому пользователю.
Аутентификация на клиенте
Нужно совсем немного кода, чтобы получить аутентифицированного в parse.com пользователя.
Подключить виджет uLogin
<script src="//ulogin.ru/js/ulogin.js"></script>
<div id="uLogin" data-ulogin="display=small;fields=first_name,last_name;providers=vkontakte,odnoklassniki,mailru,facebook;hidden=other;redirect_uri=&callback=authCallback"></div>
Добавить код callback-функции.
var user;
window.authCallback = function (token) {
Parse.Cloud.run('getCredentials', {token: token}, {
success: function (data) {
Parse.User.logIn(data.username, data.password, {
success: function () {
user = Parse.User.current();
},
error: function (user, error) {
//handle it
}
});
}
});
};
Здесь происходит вызов написанной нами ранее серверной функции "getCredentials" в Cloud Code.
При следующем заходе пользователя с помощью var user = Parse.User.current(); можно определить, аутентифицирован пользователь или нет. Если да, то виджет uLogin следует скрыть.
Возможно написание других клиентов к тем же общедоступным данным. Если клиентская сторона позволяет аутентифицировать пользователя через uLogin, то нет никаких преград, чтобы управлять данными пользователя. Недостаток в том, что лимит на количество запросов общий.
Посмотрев на imhonet, bookmix, livelib у меня появилась мысль сделать свой сервис для хранения прочитанных книг. Главная цель которого — список книг, без лишних довесов вроде оценок, покупок, обложек. При выборе подобных сервисов, интересует надежность хранения данных, чтобы не вышло так, что проект закроется через год-другой, а данные будут утрачены. Поэтому при разработке своего сервиса я решил использовать внешние бесплатные хранилища данных, то есть BaaS. Это позволит не оплачивать
Некоторые могут сказать, что регистрация пользователей только через соц. сети может повлечь серьезные трудности по восстановлению доступа к проекту в случае утраты доступа к соц. сети. Эту проблему можно было бы решить, настроив uLogin так, чтобы он спрашивал email пользователя (и сам верифицировал его), а затем восстанавливать доступ по email. Но в демо-проекте такого нет, так как список прочитанных книг общедоступен, а значит, в случае серьезных проблем, его можно просто продублировать на другую учётную запись.
Исходный код [10]
Демо [11]
P.S.: Демо — это мой продакшн, выключен не будет. От хабраэффекта возможны ошибки 155 (RequestLimitExceeded) при загрузке данных — это нормально, просто попробуйте позже.
Автор: getId
Источник [12]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/javascript/80293
Ссылки в тексте:
[1] Parse.com: https://parse.com/
[2] Image: http://habrastorage.org/files/09b/263/1a4/09b2631a44fe4ddb9c1d37cc1429471a.png
[3] не для: https://www.parse.com/questions/twitter-login-via-javascript-api
[4] javascript: https://www.parse.com/questions/twitter-login-connect-sign-in-with-javascript-sdk
[5] Cloud Code: https://parse.com/docs/cloud_code_guide#functions
[6] uLogin: https://ulogin.ru/
[7] настроить: https://ulogin.ru/help.php#fields
[8] Готовый Cloud Code: https://github.com/rnixik/knigopis.com/blob/master/cloud/main.js
[9] хостинг: https://www.reg.ru/?rlink=reflink-717
[10] Исходный код: https://github.com/rnixik/knigopis.com
[11] Демо: http://www.knigopis.com/
[12] Источник: http://habrahabr.ru/post/248511/
Нажмите здесь для печати.