суббота, 15 октября 2011 г.

Опыт работы с Lift: часть 1

После доклада на ScalaSPB образовалась подборка материалов, которыми грех не воспользоваться. Заодно включу в них то, что никак не вписывалось в доклад: примеры с кодом и философские рассуждения. И начну я пожалуй с наиболее простого и короткого кусочка, а именно опыта общения с Lift.

(Тут нужна небольшая вводная: речь идёт об опыте разработки REST сервиса. Он во-первых заведует преобразованиями между domain model и схемой хранения данных. Во-вторых по ходу дела планирует и выполняет всякие дополнительные задачи вроде отправки оповещений на почту или шаринга контента в facebook.)

Итак, от Lift'a используется практически два модуля. Это поддержка REST-сервисов и сериализация case классов в JSON (что однако тянет с собой всю инфраструктуру работы с HTTP). В работе с ними было три эпизода, которые представляют интерес. Ниже привожу первый из них.

Нестандартная авторизация

Первая проблема, которую пришлось решить это не совсем стандартная схема авторизации, используемая в нашем приложении. А именно: все экспортируемые методы делятся на два класса: приватные и публичные. Приватные защищаются на уровне файрвола от внешнего доступа и к ним на уровне приложения доступ свободный. Публичные доступны всем и соответственно на них висит авторизация. Авторизация не простая а по токену, добавленному одновременно в куку и параметр запроса. Это не что иное как издревле известный метод борьбы с CSRF. Придумать легко, а вот обучить Lift такому поведению потребовало немного нестандартного приёма.

Дело в том, что лифтовая система авторизации состоит из двух компонентов: функции-делегата, которая должна для каждого запроса указывать роли пользователей, которые имеют права на его исполнения и обработчика авторизации - который для новой сессии формирует список ролей в которых находится открывший её пользователь. Как видно она достаточно ортогональна тому поведению которое нам требовалось.

Для того, чтобы под неё подстроиться была выбрана следующая стратегия:

  1. Помечать часть путей внутри сервиса, как требующие авторизации требуя для них определённую "волшебную" роль;
  2. Выполнять аутентификацию при наличии токенов путём присваивания текущему пользователю двух ролей: "волшебной" и роли с его идентификатором в качестве имени;
  3. Предоставлять нашему коду абстракцию над предыдущими двумя шагами вида "получить авторизованного для данного метода пользователя". Этот метод изящно реализовывался паттерн-матчингом над списком ролей :)

После этого встала другая проблема. При отсутствии подходящей аутентификации Lift по умолчанию отправляет браузеру просьбу предоставить учётные данные. Это естественно совершенно недопустимо, особенно учитывая, что часть клиентов - браузерные плагины и предложение ввести учётные данные получали бы живые пользователи. Для того, чтобы этого избежать был сооружён маленький хак, который даже уже помог ещё одному человеку.

Ну а рассказ про замеченые проблемы с производительностью будет в следующем псто...

Комментариев нет: