Действительно ли сессии нарушают RESTfulness?

491

Использование сеансов в RESTful API действительно нарушает RESTfulness? Я видел много мнений, идущих в обоих направлениях, но я не уверен, что сессии проходят без REST . С моей точки зрения:

  • проверка подлинности не запрещена для RESTfulness (в противном случае было бы мало пользы в службах RESTful)
  • аутентификация выполняется путем отправки токена аутентификации в запросе, обычно заголовка
  • этот токен аутентификации должен быть получен каким-либо образом и может быть отозван, и в этом случае его необходимо обновить
  • маркер аутентификации должен быть проверен сервером (иначе это не будет аутентификация)

Так как сессии нарушают это?

  • на стороне клиента сеансы осуществляются с использованием файлов cookie
  • куки - это просто дополнительный заголовок HTTP
  • cookie-файл сессии может быть получен и отозван в любое время
  • сеансовые куки могут иметь неограниченное время жизни, если это необходимо
  • идентификатор сеанса (токен аутентификации) проверяется на стороне сервера

Таким образом, для клиента cookie сеанса точно такой же, как и любой другой механизм аутентификации на основе заголовка HTTP, за исключением того, что он использует Cookieзаголовок вместо Authorizationили какой-либо другой частный заголовок. Если к серверной части значения cookie не был подключен сеанс, почему это могло бы изменить ситуацию? Реализация на стороне сервера не должна касаться клиента, пока сервер ведет себя RESTful. Таким образом, куки сами по себе не должны создавать API без REST , а сеансы - это просто куки для клиента.

Мои предположения неверны? Что делает сессионные куки RESTless ?

децезе
источник
5
Я рассмотрел эту проблему здесь: stackoverflow.com/questions/1296421/rest-complex-applications/…
Уилл Хартунг
5
В дополнение к этому, если вы используете сеанс только для аутентификации, то почему бы не использовать предоставленные заголовки? Если нет, и вы используете сеанс для другого состояния диалога, то это нарушает ограничение REST без сохранения состояния.
Уилл Хартунг
2
@ Спасибо. Кажется, вы говорите о сессиях для временного хранения пользовательских данных, а в моем случае я просто говорю о них как о деталях реализации аутентификации. Может ли это быть причиной разногласий?
deceze
3
@deceze Единственное, что я хочу сказать, это то, что если вы собираетесь использовать заголовок для представления токена аутентификации, HTTP предоставляет его помимо общего cookie. Итак, почему бы не использовать это и сохранить бесплатную семантику, которую вы получаете с ней (любой, кто видит полезную нагрузку, может увидеть, что ей назначен токен аутентификации).
Уилл Хартунг
7
Конечно, но тогда почему бы не создать свои собственные заголовки или перехватить какой-нибудь другой заголовок для токена аутентификации. Используйте заголовок X-XYZZY. Это просто синтаксис, верно? Заголовки передают информацию. Заголовок авторизации является более «самодокументируемым», чем ваш файл cookie, потому что «все» знают, для чего предназначен заголовок Auth. Если они просто видят JSESSIONID (или что-то еще), они не могут делать никаких предположений или, что еще хуже, делать неправильные предположения (что еще он хранит в сеансе, для чего еще это используется и т. Д.). Вы называете свои переменные в своем коде Aq12hsg? Нет, конечно нет. То же самое относится и здесь.
Уилл Хартунг

Ответы:

299

Сначала давайте определим некоторые термины:

  • RESTful:

    Можно охарактеризовать приложения, соответствующие ограничениям REST, описанным в этом разделе, как «RESTful». [15] Если служба нарушает какое-либо из обязательных ограничений, ее нельзя считать RESTful.

    согласно википедии .

  • ограничение без гражданства:

    Далее мы добавим ограничение к взаимодействию клиент-сервер: связь должна быть без состояния по своей природе, как в стиле клиент-сервер без состояния (CSS) в разделе 3.4.3 (рисунок 5-3), так что каждый запрос от клиента к Сервер должен содержать всю информацию, необходимую для понимания запроса, и не может использовать какой-либо сохраненный контекст на сервере. Поэтому состояние сеанса полностью сохраняется на клиенте.

    по Филдинговой диссертации .

Таким образом, сеансы на стороне сервера нарушают ограничение REST без сохранения состояния, и поэтому RESTfulness тоже.

Таким образом, для клиента cookie сеанса точно такой же, как и любой другой механизм аутентификации на основе заголовка HTTP, за исключением того, что он использует заголовок Cookie вместо Авторизации или какого-либо другого проприетарного заголовка.

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

На мой взгляд, нет ничего плохого в печенье. Технология cookie - это механизм хранения на стороне клиента, в котором хранимые данные автоматически присоединяются к заголовкам cookie при каждом запросе. Я не знаю ограничения REST, которое имеет проблемы с такими технологиями. Таким образом, нет никаких проблем с самой технологией, проблема с ее использованием. Филдинг написал подраздел о том, почему он считает, что HTTP-куки - это плохо.

С моей точки зрения:

  • проверка подлинности не запрещена для RESTfulness (в противном случае было бы мало пользы в службах RESTful)
  • аутентификация выполняется путем отправки токена аутентификации в запросе, обычно заголовка
  • этот токен аутентификации должен быть получен каким-либо образом и может быть отозван, и в этом случае его необходимо обновить
  • маркер аутентификации должен быть проверен сервером (иначе это не будет аутентификация)

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

Рисунок 1. - Аутентификация без сохранения состояния доверенными клиентами

  • Рисунок 1. - Аутентификация без сохранения состояния доверенными клиентами

Вероятно, вам нужен кэш авторизации в памяти на стороне сервера, чтобы ускорить процесс, поскольку вы должны аутентифицировать каждый запрос.

Теперь это работает довольно хорошо доверенными клиентами, написанными вами, но как насчет сторонних клиентов? Они не могут иметь имя пользователя и пароль, а также все разрешения пользователей. Таким образом, вы должны отдельно хранить разрешения, которые может иметь сторонний клиент для конкретного пользователя. Таким образом, разработчики клиентов могут зарегистрировать своих сторонних клиентов и получить уникальный ключ API, а пользователи могут разрешить сторонним клиентам получать доступ к некоторой части своих разрешений. Например, чтение имени и адреса электронной почты, перечисление их друзей и т. Д. После разрешения стороннего клиента сервер сгенерирует токен доступа. Эти токены доступа могут использоваться сторонним клиентом для доступа к разрешениям, предоставленным пользователем, например:

Рисунок 2. - Аутентификация без сохранения состояния сторонними клиентами

  • Рисунок 2. - Аутентификация без сохранения состояния сторонними клиентами

Таким образом, сторонний клиент может получить токен доступа от доверенного клиента (или напрямую от пользователя). После этого он может отправить действительный запрос с ключом API и токеном доступа. Это самый основной сторонний механизм аутентификации. Вы можете прочитать больше о деталях реализации в документации каждой сторонней системы аутентификации, например, OAuth. Конечно, это может быть более сложным и более безопасным, например, вы можете подписать детали каждого отдельного запроса на стороне сервера и отправить подпись вместе с запросом и т. Д. Фактическое решение зависит от потребностей вашего приложения.

inf3rno
источник
5
Да, вы совершенно правы. Так как я разместил этот вопрос, я полностью пришел к тому, чтобы увидеть это. Сеансовые куки не являются чем-то особенным, если смотреть на них в технических деталях, но для деревьев не хватает леса. Принял ваш ответ из-за хороших графиков. ;)
deceze
1
Хорошо, я переосмыслил, ответ службы REST не должен зависеть от авторизации, поэтому я думаю, что первые 2 решения на 100% в порядке, а остальные в порядке, если служба использует информацию только для того, чтобы решить, разрешить ли она запрос или не. Поэтому я думаю, что права пользователя должны влиять на представление текущего ресурса.
inf3rno
1
Я создам вопрос о зависимости разрешений представлений. Я расширю этот ответ, как только получу правильное решение.
inf3rno
3
@ inf3rno, это правда, что полностью RESTful-сервис не может зависеть от сеансовых cookie-файлов для аутентификации в том виде, в котором она традиционно реализуется. Однако вы можете использовать куки для выполнения аутентификации, если куки содержат всю информацию о состоянии, которая понадобится серверу позже. Вы также можете обезопасить cookie-файл от взлома, подписав его парой открытого и закрытого ключей. Смотрите мои комментарии ниже.
jcoffland
3
Я не понимаю, почему все, кажется, принимают комментарий, вы должны хранить пароли на стороне клиента и отправлять их при каждом запросе. Это очень плохая практика, которая ставит под угрозу конфиденциальные данные ваших клиентов. Неразделенный пароль (который он должен был бы отправлять снова и снова) никогда не должен храниться где-либо. Если мы примем это, то вы будете использовать токены, как это делает большинство систем аутентификации, и в этом случае любой механизм, который мы используем для масштабирования репозитория токенов, будет иметь в основном такие же проблемы масштабируемости, как и любая масштабируемость сеанса.
lvoelk
334

Прежде всего, REST не является религией и не должен рассматриваться как таковой. Хотя у сервисов RESTful есть свои преимущества, вы должны следовать принципам REST только в той мере, в которой они имеют смысл для вашего приложения.

При этом аутентификация и состояние на стороне клиента не нарушают принципы REST. В то время как REST требует, чтобы переходы состояний были без сохранения состояния, это относится к самому серверу. В основе всего REST лежит документация. Идея безгражданства заключается в том, что СЕРВЕР - это не клиент, а не клиент. Любой клиент, отправляющий идентичный запрос (те же заголовки, файлы cookie, URI и т. Д.), Должен находиться в одном месте приложения. Если веб-сайт сохранил текущее местоположение пользователя и управлял навигацией, обновив эту переменную навигации на стороне сервера, тогда REST будет нарушен. Другой клиент с идентичной информацией запроса будет доставлен в другое место в зависимости от состояния на стороне сервера.

Веб-сервисы Google являются фантастическим примером системы RESTful. Они требуют, чтобы заголовок аутентификации с ключом аутентификации пользователя передавался при каждом запросе. Это немного нарушает принципы REST, поскольку сервер отслеживает состояние ключа аутентификации. Состояние этого ключа должно поддерживаться, и у него есть какая-то дата / время истечения, после которого он больше не предоставляет доступ. Однако, как я уже упоминал в начале своего поста, жертвы должны быть принесены, чтобы приложение действительно работало. При этом токены аутентификации должны храниться таким образом, чтобы все возможные клиенты продолжали предоставлять доступ в течение их действительного времени. Если один сервер управляет состоянием ключа аутентификации до такой степени, что другой сервер с балансировкой нагрузки не может принять выполненные запросы на основе этого ключа, Вы начали действительно нарушать принципы ОТДЫХА. Службы Google гарантируют, что вы в любое время сможете взять токен аутентификации, который вы использовали на своем телефоне, с сервером A для балансировки нагрузки, а также на сервер B для балансировки нагрузки со своего рабочего стола, и при этом иметь доступ к системе и быть направленным на те же ресурсы, если запросы были идентичны.

Все это сводится к тому, что вам необходимо убедиться, что ваши токены аутентификации проверены в каком-либо резервном хранилище (база данных, кеш и т. Д.), Чтобы обеспечить сохранение как можно большего количества свойств REST.

Я надеюсь, что все это имело смысл. Вам также следует ознакомиться с разделом «Ограничения» статьи в Википедии о передаче представительского состояния, если вы этого еще не сделали. Это особенно поучительно в отношении того, о чем на самом деле спорят принципы REST и почему.

Джаред Хардинг
источник
6
Я бы перефразировал ваше первоначальное утверждение. Используйте REST только в том случае, если ограничения REST имеют смысл для вашего приложения. Вы можете применить подмножество этих ограничений, и вы получите подмножество преимуществ. Однако на тот момент вы создали свой собственный архитектурный стиль. Впрочем, это неплохо, ведь именно об этом и говорится в первых четырех главах диссертации Роя о принципиальном замысле. ОТДЫХ был только одним примером.
Даррел Миллер
4
@Darrel Достаточно справедливое замечание. Я, честно говоря, не уверен, как Google это делает, но время истечения может быть закодировано в токен аутентификации. Я считаю, что моя большая точка все еще остается в силе Существуют некоторые типы состояний, которые просто необходимо поддерживать, и, пока вы понимаете, почему REST требует безгражданства, вы можете нарушать его таким образом, чтобы это имело смысл без множества последствий для остальной системы и преимуществ архитектуры RESTful. ,
Джаред Хардинг
7
Поскольку никаких других аргументов до сих пор не выдвинуто, я принимаю этот хорошо написанный ответ. Я думаю, что важной частью является то, что сервер без сохранения состояния не означает сервер без сохранения состояния , что, как мне кажется, часто неправильно понимается или применяется неправильно. Сервер может (и обычно должен ) иметь любое желаемое состояние, если он ведет себя идемпотентно .
deceze
10
Я слышал так много проповедей, что сессии не успокаивают. Базовая аутентификация HTTP - это настоящий шаг назад, если вы пытаетесь создать веб-приложение.
Бен Терли
1
@Micah Henning, вы делаете ложное предположение, что серверу требуется информация о состоянии для проверки токена аутентификации. Мы можем разумно предположить, что вы не можете подделать токен, который был подписан парой открытого и закрытого ключей, если вы не знаете секретный ключ. Чтобы убедиться, что токен действителен, вам нужен только открытый ключ. Я до сих пор считаю, что полностью RESTful-аутентификация возможна.
jcoffland
12

Cookies не для аутентификации. Зачем изобретать велосипед? HTTP имеет хорошо разработанные механизмы аутентификации. Если мы используем файлы cookie, мы используем HTTP только в качестве транспортного протокола, поэтому нам нужно создать собственную систему сигнализации, например, чтобы сообщать пользователям, что они предоставили неверную аутентификацию (использование HTTP 401 было бы неправильным, поскольку мы, вероятно, не будем поставлять Www-Authenticateклиенту, как того требуют спецификации HTTP :)). Следует также отметить, что Set-Cookieэто только рекомендация для клиента. Его содержимое может быть сохранено или не сохранено (например, если файлы cookie отключены), а Authorizationзаголовок отправляется автоматически при каждом запросе.

Другой момент заключается в том, что для получения авторизационного cookie вы, вероятно, захотите сначала указать свои учетные данные? Если так, то не будет ли это без RESTless? Простой пример:

  • Вы пытаетесь GET /aбез cookie
  • Вы получаете запрос авторизации как-то
  • Вы идете и авторизуетесь как-то POST /auth
  • Ты получаешь Set-Cookie
  • Вы пытаетесь GET /a с cookie. Но GET /aведет ли себя идемпотентно в этом случае?

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

starteleport
источник
1
В то же время я пришел к этой точке зрения больше. Я думаю, что технически это не имеет большого значения, это всего лишь заголовки HTTP. Правда, само поведение аутентификации не является RESTful, если требуется вход через отдельный адрес. Таким образом, файлы cookie являются лишь признаком более серьезной проблемы с системой аутентификации.
deceze
Это на самом деле не объясняет тот факт, что веб-браузеры поддерживают только Authorization: Basicили Digest. Если вы хотите сделать что-то более продвинутое, чем обычная или дайджест-аутентификация (и вы должны это делать) в контексте браузера, тогда вам понадобится что-то кроме Authorizationзаголовка.
Оливер
1
Абсолютно - если вы делаете чистый JS, то все в порядке (за исключением, например, Websockets). Но я хочу сказать, что аутентификация на основе API не обязательно является единственным соображением в сценарии браузера.
Оливер
5
GET /aбез cookie и с cookie явно два разных запроса, и для них приемлемо вести себя по-разному.
TRiG
1
Чтобы добавить к @TRiG, ​​следуя этой логике, GET /aзаголовок аутентификации также не отличается GET /aот заголовка аутентификации, что делает его одинаково непригодным для REST. Если вы собираетесь обрабатывать один http-заголовок иначе, чем другой, вы по крайней мере решите эту проблему.
Джаспер
7

На самом деле, RESTfulness применяется только к RESOURCES, как указано универсальным идентификатором ресурса. Так что даже говорить о таких вещах, как заголовки, куки и т. Д. В отношении REST, не совсем уместно. REST может работать по любому протоколу, даже если он обычно выполняется по HTTP.

Основным определителем является следующее: если вы отправляете вызов REST, который является URI, то после успешного вызова на сервер этот URI возвращает тот же контент, предполагая, что переходы не были выполнены (PUT, POST, DELETE) ? Этот тест исключил бы возврат ошибок или запросов на аутентификацию, поскольку в этом случае запрос еще не поступил на сервер, то есть сервлет или приложение, которое будет возвращать документ, соответствующий данному URI.

Аналогично, в случае POST или PUT, можете ли вы отправить определенный URI / полезную нагрузку, и независимо от того, сколько раз вы отправляете сообщение, оно всегда будет обновлять одни и те же данные, чтобы последующие GET возвращали согласованный результат?

REST относится к данным приложения, а не к низкоуровневой информации, необходимой для передачи этих данных.

В следующем сообщении в блоге Рой Филдинг дал хорошее резюме всей идеи REST:

http://groups.yahoo.com/neo/groups/rest-discuss/conversations/topics/5841

«Система RESTful переходит от одного устойчивого состояния к следующему, и каждое такое устойчивое состояние является одновременно потенциальным начальным состоянием и потенциальным конечным состоянием. То есть система RESTful представляет собой неизвестное число компонентов, подчиняющихся простому набору правила таковы, что они всегда находятся в состоянии REST или переходят из одного состояния RESTful в другое состояние RESTful. Каждое состояние может быть полностью понято представлением (ями), которые оно содержит, и набором переходов, которые оно обеспечивает, с переходами, ограниченными униформой набор действий, которые должны быть понятны. Система может представлять собой сложную диаграмму состояний, но каждый пользовательский агент может видеть только одно состояние за раз (текущее устойчивое состояние), и, таким образом, каждое состояние является простым и может анализироваться независимо. Пользователь OTOH может создавать свои собственные переходы в любое время (например, ввести URL, выбрать закладку,открыть редактор и т. д.).


Переходя к вопросу аутентификации, независимо от того, выполняется ли она с помощью файлов cookie или заголовков, если информация не является частью полезной нагрузки URI и POST, она на самом деле не имеет ничего общего с REST. Итак, что касается отсутствия гражданства, мы говорим только о данных приложения.

Например, когда пользователь вводит данные в экран графического интерфейса, клиент отслеживает, какие поля были введены, а какие нет, какие-либо обязательные поля отсутствуют и т. Д. Это все КЛИЕНТСКИЙ КОНТЕКСТ, и его не следует отправлять или отслеживать на сервере. На сервер отправляется полный набор полей, которые необходимо изменить в ресурсе IDENTIFIED (посредством URI), чтобы в этом ресурсе происходил переход из одного состояния RESTful в другое.

Таким образом, клиент отслеживает действия пользователя и отправляет на сервер только логически завершенные переходы состояний.

Кен Копельсон
источник
3
Я не понимаю, как это проливает свет на поставленный вопрос.
jcoffland
1

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

Вы могли бы обойтись без конкатенации следующим образом: usernameRole: password, но это плохая практика, и она также неэффективна, потому что, когда у пользователя больше ролей, механизму аутентификации потребуется проверять все роли в конкатенации и повторять каждый вызов. Это уничтожило бы одно из самых больших технических преимуществ RBAC, а именно очень быстрый тест авторизации.

Так что эта проблема не может быть решена с помощью обычной аутентификации доступа.

Чтобы решить эту проблему, необходимо поддерживать сессию, и это, согласно некоторым ответам, противоречит REST.

Вот что мне нравится в ответе, что REST не следует рассматривать как религию. Например, в сложных бизнес-ситуациях, например, в здравоохранении, RBAC абсолютно распространен и необходим. И было бы жаль, если бы им не разрешили использовать REST, потому что все разработчики инструментов REST рассматривали бы REST как религию.

Для меня не так много способов поддерживать сеанс через HTTP. Можно использовать куки с sessionId или заголовок с sessionId.

Если у кого-то есть другая идея, я буду рад ее услышать.

Берт Верхи
источник
-4
  1. Сессии не без отдыха
  2. Вы имеете в виду, что сервис REST только для http-использования или я что-то не так понял? Сеанс на основе файлов cookie должен использоваться только для собственных (!) Сервисов на основе http! (Это может быть проблемой для работы с cookie, например, из Mobile / Console / Desktop / etc.)
  3. если вы предоставляете услугу RESTful для сторонних разработчиков, никогда не используйте сеанс на основе файлов cookie, вместо этого используйте токены, чтобы избежать проблем с безопасностью.
Максим
источник
3
cookie не следует использовать для хранения ключа сеанса для сеанса на сервере, который содержит токен аутентификации. но если cookie содержит сам маркер аутентификации, это реальное решение. (конечно, печенье должно быть httponly и защищено)
roberkules