В этом посте я не буду рассказывать про API для работы с сессиями, это можно найти в документации к Mojolicious, а постараюсь объяснить как устроены сессии внутри.
Mojolicious использует сookies для хранения сессий. И это достаточно важный момент. Такой подход позволяет нам следовать REST идеологии. Мы можем отказаться от лишних состояний на стороне сервера, нам не нужно обеспечивать общего пространства для доступа к сессиям с разных серверов.
Но имеется несколько важных нюансов:
Мы оставляем документ в открытом виде и лишь добавляем к нему подпись(цифровую или ручкой на бумаге)
Подпись создаем следующим образом: md5_hex("foobar" . $secret_key). Если $secret_key равен "mysecret", то мы получим подпись вида - "c232332be32fec146ef0fdb82bb913d2". Подписанный документ может выглядеть так - "foobar---c232332be32fec146ef0fdb82bb913d2". И если кто-то попробует изменить текст с "foobar" на "barfoo", то он сможет это сделать, но не сможет переподписать измененный документ, поскольку он не знает $secret_key.
Сразу оговорюсь, что в классическом варианте электронной цифровой подписи используются ассиметричное шифрование для того, чтобы предоставить третим лицам возможность сверки подписи. Поскольку в нашем случая сверять подпись будет только ее автор, то для нас это не актуально.
Таким образом, сессия сохраненная в cookies доступна для чтения на клиентской стороне ( при помощи javascript ), но недоступна для изменения.
Использование Storable для сериализации позволяет выиграть в скорости, но лишает нас возможности читать данные сессии на стороне клиента( при помощи javascript ). Использование формата JSON дало бы нам эту возможность.
UPD: Если работа с сессией предполагается на двух или более серверах, Storable принесет за собой еще один подводный камень. Если по какой-то причине версии этого модуля на машинах станут разными, данные перестанут распаковываться. Об этом придется помнить всегда.
Mojolicious использует сookies для хранения сессий. И это достаточно важный момент. Такой подход позволяет нам следовать REST идеологии. Мы можем отказаться от лишних состояний на стороне сервера, нам не нужно обеспечивать общего пространства для доступа к сессиям с разных серверов.
Но имеется несколько важных нюансов:
- Старайтесь не делать сессии большими, поскольку эти данные будут передаваться при каждом запросе, а максимально безопасный размер cookie - 4кб.
- Вопрос авторства данных.
Что должна обеспечивать подпись?
Начнем с того, что любая подпись (включая подпись ручкой на бумаге) должна обеспечить следующие две вещи:- Идентифицировать автора подписи.
- Гарантировать, что после того, как документ подписали, он не изменялся.
Мы оставляем документ в открытом виде и лишь добавляем к нему подпись(цифровую или ручкой на бумаге)
Стандартная реализация
Допустим, нам необходимо подписать текст "foobar".Подпись создаем следующим образом: md5_hex("foobar" . $secret_key). Если $secret_key равен "mysecret", то мы получим подпись вида - "c232332be32fec146ef0fdb82bb913d2". Подписанный документ может выглядеть так - "foobar---c232332be32fec146ef0fdb82bb913d2". И если кто-то попробует изменить текст с "foobar" на "barfoo", то он сможет это сделать, но не сможет переподписать измененный документ, поскольку он не знает $secret_key.
Сразу оговорюсь, что в классическом варианте электронной цифровой подписи используются ассиметричное шифрование для того, чтобы предоставить третим лицам возможность сверки подписи. Поскольку в нашем случая сверять подпись будет только ее автор, то для нас это не актуально.
Таким образом, сессия сохраненная в cookies доступна для чтения на клиентской стороне ( при помощи javascript ), но недоступна для изменения.
Необходимо обратить внимание на следующие вещи:
1. Если кто-то узнал $secret_key, то его необходимо изменить, и это автоматически сделает все сессии недействительными.
2. Поскольку сессии не хранятся на сервере, то нельзя просто взять и удалить файл. И для того, чтобы сессия не была вечно действительна в нее добавляют временную метку, которая определяет время жизни сессии.
Как реализованы сессии в Mojolicious?
Вот как выглядит cookie с сессией в Mojolicious приложении -
"mojolicious=BAcIMTIzNDU2NzgECAgIAwMAAAAKATUHAAAAdXNlcl9pZAaLg2lNAAAAAAcAAABleHBpcmVzCgd0ZXN0MTAwBQAAAGxvZ2lu--6bdbaee00738ed3c2793d518857cfc9b"
И получили мы это приблизительно ( реальный код можно посмотреть в Mojolicious::Sessions и Mojolicious::Controller ) таким образом:
use Storable qw/freeze/; use Mojo::Util qw/b64_encode hmac_md5_sum/; sub store_session { # Get controller instance my $self = shift; # Get session hashref my $session = $self->stash('mojo.session'); # Set session expiration my $expires = time + $self->default_expiration(); $session->{expires} = $expires; # Serialize session $value = b64_encode( freeze($session) ); $value =~ s/\=/\-/g; # Secret my $secret = $self->app->secret; # Sign session my $signature = hmac_md5_sum( $value, $secret ); $value .= "--$signature"; # Create cookie my $cookie = $self->cookie('mojolicious', $value, { expires => $expires }); }
Использование Storable для сериализации позволяет выиграть в скорости, но лишает нас возможности читать данные сессии на стороне клиента( при помощи javascript ). Использование формата JSON дало бы нам эту возможность.
UPD: Если работа с сессией предполагается на двух или более серверах, Storable принесет за собой еще один подводный камень. Если по какой-то причине версии этого модуля на машинах станут разными, данные перестанут распаковываться. Об этом придется помнить всегда.
Предыдущие посты про Mojolicious:
9 коммент.:
Если работа с сессией предполагается на двух или более серверах, Storable принесет за собой еще один подводный камень. Если по какой-то причине версии этого модуля на машинах станут разными, данные перестанут распаковываться. Об этом придется помнить всегда.
> Если работа с сессией предполагается на двух или более серверах, Storable принесет за собой еще один подводный камень. Если по какой-то причине версии этого модуля на машинах станут разными, данные перестанут распаковываться. Об этом придется помнить всегда.
Спасибо, дельное замечание, добавил в пост
> Использование Storable для сериализации позволяет выиграть в скорости
JSON::XS быстрее, и его не надо будет base64
> $value = b64_encode( freeze($session) );
Если уж беспокоиться о переносимости сессий между машинами, то лучше им было взять nfreeze()
Что касается вообще сессий на клиенте, то мне плюсы (REST + ненужность серверного хранилища) кажутся сильно небольшими по сравнению с минусами.
Первое — трафик. То, что сессия должна влазить в 4Кб (за вычетом других кук, кстати), это полбеды. Пусть даже она будет на 300 байт больше, чем обычный идентификатор.
Так вот, страничка этого блога требует 11 реквестов. И с каждым идет красивая кука, хотя ее не просят. 3300 байт. Миллион хитов — сто гигов в месяц сверху. Оно надо? Можно, правда, path потюнить, но это не радикальное средство.
Второе, и очень важное.
> Поскольку сессии не хранятся на сервере, то нельзя просто взять и удалить файл
Очень зря. Как я понимаю, это не дает возможности сделать logout everywhere в случае дискредитации аккаунта или по желанию пользователя. Это неприятный косяк.
> Так вот, страничка этого блога требует 11 реквестов. И с каждым идет красивая кука, хотя ее не просят. 3300 байт. Миллион хитов — сто гигов в месяц сверху. Оно надо? Можно, правда, path потюнить, но это не радикальное средство.
Если это стает проблемой, то нужно вынести статику в отдельный домен.
Посмотрел( через firebug ) на запросы этой странички - их 47 штук(если страница не была еще закеширована), и с 47 всего 2 запроса пришлось на домен koorchik.blogspot.com, то есть кука с сессией передается всего 2 раза. Общий размер трафика - 455 кбайт. Если сессия 300 байт, то ее относительный размер составит 0,06% трафика. Что совершенно несущественно.
> Очень зря. Как я понимаю, это не дает возможности сделать logout everywhere в случае дискредитации аккаунта или по желанию пользователя. Это неприятный косяк.
Косяк. Но если нужен logout everywhere, то это можно реализовать следующим образом: хранить некое связанное с пользователем число, которое при логине сохраняется в сессию, если число у юзера и в сесси не совпадает, то сессия недействительна.
> нужно вынести статику в отдельный домен
Ну да, домены и path-ы. Конечно, it depends, но, например, для мобильных версий все равно проблема. Там каждый килобайт на счету.
> хранить некое связанное с пользователем число
То есть хранить state на сервере, ура-ура :)
> То есть хранить state на сервере, ура-ура :) По большому счету, любые данные пользователя можно рассматривать как его состояние. А хранение некого числа связанного с пользователем это лишь еще один вид данных. Вся выгода в том, что это число не обязано быть доступным на всех серверах.
Мы можем реализовать шардинг и разбросать сервера по всему миру, и не переживать за общее хранилище сессий.
Ну а если в общем, то все конечно зависит от задач :)
2alienator
> JSON::XS быстрее, и его не надо будет base64
Просто Mojolicious без внешних зависимостей, но воможно кто-то еще реализует плагин для сессий с использованием JSON::XS. Кроме того Storable позволяет в сессию сохранить блеснутый объект.
>Если уж беспокоиться о переносимости сессий между машинами, то лучше им было взять nfreeze()
Cам был удивлен тем, что используется freeze :)
Хорошая статья... Как раз решаю этот вопрос. Но комменты убедили меня хранить сессии в Mysql.memory
Отправить комментарий
Не забудьте добавить себя в постоянные читатели и включить уведомления о новых комментариях, либо воспользуйтесь RSS каналом ;)