Различные веб-фреймворки и языки по-разному подходят к вопросу, как хранить сессию пользователя, да и просто:
как её привязать к пользователю. Особые проблемы начинаются тогда, когда надо связывать разные технологии, например
сделать единую авторизацию для рельсового приложения, phpBB форума и стриминг-сервера.
Самый простой подход — немного доработанные рельсовые сессии.
Идея состоит в том, что вся информация, которая уходит пользователю по нешифрованному каналу итак открыта, поэтому
нечего бояться её показать. Плюс очень, очень не рекомендуется гонять в сессии целые объекты (как это сделано с
flash[:cart] в Agile Web Development On Rails).
Итак, в сессию складывается относительно небольшой объём информации: user_id, last_order_id (если нужно) и т.п.
Ничего страшного в том, что бы запалить id пользователя нет.
В рельсах такая сессия выглядит как хеш-таблица:
{:user_id => 15}которая упакована с помощью Marshal — это универсальный сериализатор всех руби объектов, потом Base64 кодирована для
того, что бы можно было безопасно убрать в куку.
Вопрос: а как же удостовериться в том, что плохой пользователь не проделает то же самое и не присвоит себе
user_id админа?
Base64-кодированная сессия подписывается секретным ключом с помощью SHA1 и подпись подклеивается к сессии с разделителем: “—”. Вот пример такой куки:
BAh7CToPc2Vzc2lvbl9pZCIlNWQ3Y2EzNDBmZmM3ZDdiMWQzMzdiYmIyODI1MzA3NWM6DHVzZXJfaWRpAk4BOhBfY3NyZl90b2tlbiIxRmVwdWJNcT Bkd2VYekJ4MS8yVmZGb0g3Y1RyaGRyTTN3aC9ib2laTmdtaz0iCmZsYXNoSUM6J0FjdGlvbkNvbnRyb2xsZXI6OkZsYXNoOjpGbGFzaEhhc2h7BjoL bm90aWNlIhxTaWduZWQgaW4gc3VjY2Vzc2Z1bGx5LgY6CkB1c2VkewY7CUY%3D—d2ea88629596f21ea25d92c60ece7cca4841e6feДавайте её распакуем:
irb -rubygems -raction_pack -raction_controller -ractive_support encoded, sign = cookie.gsub(‘%3D’,‘=’).split(“—”) session = Marshal.load(Base64.decode64(encoded)) => {:session_id=>"5d7ca340ffc7d7b1d337bbb28253075c", :user_id=>334, :_csrf_token=>"FepubMq0dweXzBx1/2VfFoH7cTrhdrM3wh/boiZNgmk=", “flash”=>{:notice=>"Signed in successfully."}}На самом деле, уже тут кроется небольшой обман:
session[“flash”].class=> ActionController::Flash::FlashHash
С этой проблемой мы ещё столкнемся дальше.
Чем это круто? Да очень просто: пользователя можно совершенно спокойно перебросить на другой сервер, не создавая централизованное хранилище сессий. Плюс навсегда пропадает проблема зачистки старых сессий.
37 Signals, внедряя CookieStore допустили очень серьезную ошибку в одном месте: они сделали Marshal.dump вместо JSON.encode.
Давайте посмотрим, как вся эта технология будет выглядеть, если сессия будет отправляться на браузер в виде обычного подписанного JSON-а. Пользователь приходит на рельсовую морду, получает такую куку, идёт на PHP-форум, где все эти механизмы сделаны точно так же! Вуа-ля, авторизация на смежных субпроектах получена бесплатно!
Но это не всё. На странице загружается флешка, берет куку, отдает её socket-серверу или RTMP-серверу, тот распаковывает эту сессию, на каком бы языке он ни был написан: JSON откроется везде, находит там user_id и подписывает это соединение на получение сообщений для user_id!
Более того, этот механизм уже есть и работает в erlyvideo!
Проблемы начнутся в рельсах.
Во-первых, сессия в рельсах — не HashWithIndifferentAccess, а существенно более сложная штука. Символы и строки в руби —
разные сущности и в одном хеше может быть одновременно ключом как строка, так и символ и значения будут разные.
Ладно, это решаем административно: только символы и не волнует.
Во-вторых, flash. Это особый объект со своими проблемами.
Если не хочется вникать, то этот патч к MessageVerifier-у исправно работает в продакшне.
Наверное стоит начать процедуру пиления 37 Signals, что бы они сделали такую униформную штуку.
Да ничего. Я на нём 5 лет не писал, так что если кто-нибудь хочет, я помогу ему заимплементить этот механизм.
Erlyvideo с этой концепцией полностью совместим. Давайте посмотрим на пример в contrib/chat. Для него
у вас должна стоять sinatra и rails:
Меняем для vhost-а default в ebin/erlmedia.app значение auth_module на json_session:
{auth_module, json_session},и выбираем secret_key подлиннее
Теперь
ems:restart().
После этого запускаем руби-сервер:
В первом окне вы видите подключение со страницы, сгенеренной руби-сервером, во втором окне тестовый пример работы
erlyvideo.
Вы можете попробовать вводить данные в обоих окошках и початиться. Что же в этом всём необычного?
Да то, что флешка в обоих случаях не может представиться другим пользователем. Какой user_id ей отдался в сессии,
так её erlyvideo и признает, подделать не получится. Соответственно, erlyvideo подпишет именно на те каналы,
на которые надо.
Проверку на валидность посылки можно осуществить в хендлере post ‘/say’ в chat.rb