Есть сайт(сервис) по предоставлению услуг, внутри сайта есть личный кабинет пользователя, с его историей заказов, статусами заказов, историей оплат и т.д.
необходимо сделать API, которым бы пользовалось android приложение.
Как я начал делать, при авторизации с приложения делаю запрос к API такого плана:
/api/?user.checkAuth={"login":"admin", "pass":"md5('.....)"} проверяю правильный ли хеш,
А дальше генерирую случайную строку и сохраняю ее на сервере в паре СТРОКА - ПОЛЬЗОВАТЕЛЬ, так же отсылаю ее в ответ приложению, те эдакий сессионный токен, который знаю я, и знает приложение.
Далее приложение запрашивает список заказов, передавая мне только то, что знает: те токен, который я выдал при авторизации,:
запрос на список заказов:
/api/?user.getUserOrders={"token":"29db74ae5302f6fafa75428ada7f1871"}
на стороне сервера я принимаю токен, смотрю есть ли такой токен вообще к какому пользователю он привязан, и если все окей, то возвращаю JSON с заказами.
Тут первый вопрос, я двигаюсь в правильном направлении? и вытекающая проблема из текущей реализации:
1) есть у пользователя телефон и планшет с приложением
2) он авторизовался на телефоне - получил токен - пользуется
3) заходит с планшета - получает токен, КОТОРЫЙ ПЕРЕЗАПИСЫВАЕТСЯ если уже привязан к какому-то пользователю - пользуется.
вот тут и проблема, теперь если пользователь зайдет со своего телефона в приложение и попытается что-то запросить, передав токен с который сохранен в телефоне, то этот токен мы уже не найдем(тк перезаписали пару пользователь-токен при авторизации с планшета) и соответственно кинем его на страницу авторизации, для получения токена... и так до бесконечности, пользователь может работать только с 1ого устройста...
Как решить эту проблему, те сохранить безопасность, работая с токенами, и дать пользователю возможность иметь несколько токенов?
Чем-то грозит реализация, такая, что один user_id может иметь несколько токенов?
Как организовать сброс этих токенов и нужен ли он?
Шаг 1, изучаем https://jwt.io/ - на настоящий момент стандарт для аутентификации.
Шаг 2. Каждое устройство должно иметь уникальный токен. Пользователь должен иметь возможность деавторизовать любое устройство. При смене пароля все токены автоматически стираются.
Организация хранения токена должна выглядеть примерно так:
tokens
- user_id
- device_id - при авторизации через браузер можно подставить md5(User-Agent)
- device_name - человеко-понятное имя девайса или название браузера
- token
- last_used
- expires_at
Про API, вместо передачи дополнительного параметра в запросе очень часто используют HTTP-заголовки.
Наличие множества токенов практически ничем не грозит, разве что небольшим увеличением размера данных.
Сброс токенов нужен по времени, по смене пароля, значительной смене географии (другая страна и т.п.), при нажатии кнопки Выход и по желанию пользователя (опции Выйти со всех устройств).
xmoonlight: конечно можете. Если ездить по дороге не по правилам, но аккуратно, то и домой сможете попасть. По вашей логике можно отменить правила.
И насрать, что потом прийдется задолбаться делая клиент для вашего API.
Philipp T: ну вот, значит они это придумали и теперь везде пиарят как некий стандарт: суть - понятна. Про протокол TCP/IP - не будем вспоминать)
(поищите еще в Яндексе еще для полной картины...)
>Про API, вместо передачи дополнительного параметра в запросе очень часто используют HTTP-заголовки.
в этом моменте следует быть осторожным, тк ГЕТ запрос например должен быть имподентным, что может привести к ошибкам, тк сервер/клиент может такой гет запрос кэшировать (что часто и происходит, тк согласно спецификации, на один и тотже запрос должен отдаваться один и тотже ответ) в результате даже если будут разные заголовки, ответ будет один и тотже.
Вобщем если заголовки будут менять ответ, то использовать например ПОСТ запрос. . .
дима кубитский: не "имподентным", а идемпотентным. Дело в том, что не только метод GET является идемпотентным, но многие другие, например OPTIONS и даже DELETE.
Кэширование ответа не связано с REST, а связано с управлением кэшированием в целом. Для этого существуют заголовки ответа E-Tag, Expires, Pragma: nocache и т.д. Браузеры понимают их. Если вы сталкиваетесь с кэшированием ответа, то тут нужно добавлять некоторый несуществующий параметр к запросу, например `/api/user?_=UNIQUEVALUE`.
Как я начал делать, при авторизации с приложения делаю запрос к API такого плана:
/api/?user.checkAuth={"login":"admin", "pass":"md5('.....)"} проверяю правильный ли хеш,
А дальше генерирую случайную строку и сохраняю ее на сервере в паре СТРОКА - ПОЛЬЗОВАТЕЛЬ, так же отсылаю ее в ответ приложению, те эдакий сессионный токен, который знаю я, и знает приложение.
Наоборот.
Приложение стукнулось на сайт - пока без авторизации.
Сайт создает запись о новом посетителе и присваивает ему токен. Возвращает id посетителя и токен.
Приложение возвращает этот id, логин и хэш от (хэш пароля + токен) - сайт сможет повторить это хэширование, а хэш пароля (по которому у вас, получается, можно авторизоваться, даже не зная пароля) в передаче не светится вовсе.
Заодно логика посетителя позволяет одному и тому же клиенту одновременно авторизоваться с нескольких устройств, не сбрасывая сессию.
Чем-то грозит реализация, такая, что один user_id может иметь несколько токенов?
Стандартной ошибкой - если вы будете в чем-то полагаться на ту информацию, которую хранит приложение, а не сайт.
Например, если приложение при запуске загружает остаток средств на счете и потом оперирует этой цифрой, не проверяя, не был ли он за это время уже изменен на сайте.
Чисто замечание по оформлению. Запросы на выборку, где просто присутствуют ключи per_page, limit и т. п., нужно передавать методом GET, содержащие длинные или личные данные - POST и другими, по ситуации. Необходимость этого в том, что GET передает данные просто в адресной строке браузера (это очевидно), POST передает их по шифрованному туннелю (если, конечно, Вы поднимите шифрование, иначе и смысла не будет, через консоль разраба Хрома их можно вытащить в миг). Плюс ограниченная длина: у GET ограничена строкой браузера, у POST и прочих хоть книгу передавайте. Нативный cURL из коробки работает со всеми методами.