Чи справді сеанси порушують RESTfulness?


491

Чи використання сеансів в API RESTful дійсно порушує RESTfulness? Я бачив багато думок в будь-якому напрямку, але я не впевнений, що сеанси не мають сили . З моєї точки зору:

  • аутентифікація не заборонена для RESTfulness (інакше користь у послугах RESTful буде мало)
  • аутентифікація здійснюється шляхом надсилання маркера аутентифікації в запиті, як правило, в заголовку
  • цей маркер автентифікації потрібно отримати якось і може бути відкликаний, і в цьому випадку його потрібно поновити
  • маркер автентифікації повинен бути підтверджений сервером (інакше це не буде автентифікацією)

То як сеанси порушують це?

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

Таким чином, для клієнта сеансовий cookie точно такий же, як і будь-який інший механізм аутентифікації на основі заголовка HTTP, за винятком того, що він використовує Cookieзаголовок замість Authorizationіншого або власного заголовка. Якщо не було жодного сеансу, приєднаного до значення сервера файлів cookie, чому це може змінити значення? Реалізація на стороні сервера не повинна стосуватися клієнта, поки сервер веде себе RESTful. Таким чином, файли cookie самі по собі не повинні робити API RESTless , а сеанси - просто куки для клієнта.

Чи мої припущення неправильні? Що робить файли cookie сеансу RESTless ?


5
Я висвітлював цю точну проблему тут: stackoverflow.com/questions/1296421/rest-complex-applications/…
Буде Хартунг

5
Додайте до цього, якщо ви використовуєте сеанс лише для автентифікації, то чому б не використовувати надані заголовки? Якщо ні, і ви використовуєте сеанс для іншого стану розмови, це порушує обмеження REST без громадянства.
Буде Хартунг

2
@ Буду спасибі Здається, ви говорите про сеанси тимчасового зберігання поданих користувачем даних, тоді як у моєму випадку я просто говорю про них як про деталі реалізації для автентифікації. Можливо, звідки походить незгода?
деге

3
@deceze Єдиним моїм пунктом є те, що якщо ви збираєтесь використовувати заголовок для представлення маркера аутентифікації, HTTP надає його за межі загального файлу cookie. Отже, чому б не скористатися цим і зберегти безкоштовну семантику, яку ви отримуєте з ним (кожен, хто бачить корисний вантаж, може побачити, що йому призначений маркер автентифікації).
Буде Хартунг

7
Звичайно, але чому б тоді не скласти власні заголовки чи захопити якийсь інший заголовок для маркера автентичності. Використовуйте заголовок X-XYZZY. Це просто синтаксис, правда? Заголовки передають інформацію. Заголовок авторизації є більш "самодокументованим", ніж ваш cookie, оскільки "всі" знають, для чого призначений заголовок Auth. Якщо вони просто бачать JSESSIONID (чи що завгодно), вони не можуть робити жодних припущень, або, що ще гірше, робити неправильні припущення (що ще він зберігає в сеансі, для чого ще використовується тощо). Ви називаєте свої змінні у своєму коді Aq12hsg? Ні, звичайно ні. Те саме стосується і тут.
Буде Хартунг

Відповіді:


299

Спочатку визначимося з деякими термінами:

  • Відпочинок:

    Можна охарактеризувати програми, що відповідають обмеженням REST, описаним у цьому розділі як "RESTful". [15] Якщо послуга порушує будь-яке з необхідних обмежень, її не можна вважати RESTful.

    згідно з Вікіпедією .

  • обмеження без громадянства:

    Далі ми додаємо обмеження для взаємодії клієнт-сервер: зв’язок повинен мати статус без громадянства, як у стилі клієнт-стан без сервера (CSS) розділу 3.4.3 (рис. 5-3), таким чином, щоб кожен запит від клієнта до сервер повинен містити всю інформацію, необхідну для розуміння запиту, і не може скористатися будь-яким збереженим контекстом на сервері. Тому стан сесії повністю зберігається на клієнті.

    згідно дисертації Філдінга .

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

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

За допомогою сеансових файлів cookie ви зберігаєте стан клієнта на сервері, і тому ваш запит має контекст. Спробуємо додати у вашу систему балансир завантаження та інший службовий екземпляр. У цьому випадку вам потрібно розділити сеанси між екземплярами служби. Важко підтримувати та розширювати таку систему, тому вона погано масштабується ...

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

З моєї точки зору:

  • аутентифікація не заборонена для RESTfulness (інакше користь у послугах RESTful буде мало)
  • аутентифікація здійснюється шляхом надсилання маркера аутентифікації в запиті, як правило, в заголовку
  • цей маркер автентифікації потрібно отримати якось і може бути відкликаний, і в цьому випадку його потрібно поновити
  • маркер автентифікації повинен бути підтверджений сервером (інакше це не буде автентифікацією)

Ваша точка зору була досить твердою. Єдина проблема полягала в концепції створення маркера аутентифікації на сервері. Вам не потрібна ця частина. Вам потрібно зберегти ім'я користувача та пароль на клієнті та надсилати їх із кожним запитом. Для цього вам не потрібно більше ніж основне авторизація HTTP та зашифроване з'єднання:

Малюнок 1. - Аутентифікація без громадянства надійними клієнтами

  • Малюнок 1. - Аутентифікація без громадянства надійними клієнтами

Напевно, вам потрібен кеш аутентифікації пам'яті на стороні сервера, щоб зробити це швидше, оскільки вам потрібно підтвердити автентифікацію кожного запиту.

Зараз це працює досить добре надісланими вами довіреними клієнтами, а як же сторонні клієнти? Вони не можуть мати ім’я користувача та пароль, а також усі дозволи користувачів. Таким чином, ви повинні окремо зберігати, які дозволи клієнт стороннього клієнта може мати певний користувач. Таким чином, розробники клієнтів можуть зареєструвати їх сторонніх клієнтів та отримати унікальний ключ API, а користувачі можуть дозволити стороннім клієнтам отримати доступ до певної частини своїх дозволів. Як і читати ім’я та адресу електронної пошти, або перелічувати своїх друзів тощо ... Після дозволу стороннього клієнта сервер генерує маркер доступу. Ці маркери доступу можуть використовуватися стороннім клієнтом для доступу до дозволів, наданих користувачем, наприклад:

Малюнок 2. - Аутентифікація без громадянства сторонніми клієнтами

  • Малюнок 2. - Аутентифікація без громадянства сторонніми клієнтами

Тож сторонній клієнт може отримати маркер доступу від надійного клієнта (або безпосередньо від користувача). Після цього він може надіслати дійсний запит за допомогою ключа API та маркера доступу. Це найосновніший механізм аутентифікації третьої сторони. Більш детальну інформацію про реалізацію можна прочитати в документації кожної сторонньої системи авторизації, наприклад, OAuth. Звичайно, це може бути більш складним і більш безпечним, наприклад, ви можете підписати деталі кожного запиту на стороні сервера і надіслати підпис разом із запитом тощо. Фактичне рішення залежить від потреби вашої програми.


5
Так, ви абсолютно праві. Оскільки я опублікував це питання, я цілком зрозумів це. Сесіонні файли cookie не є чимось особливим, якщо їх вивчити в технічних деталях, але цього лісу немає для дерев. Прийняв вашу відповідь через приємні діаграми. ;)
обман

1
Гаразд, я передумав, відповідь служби REST не повинна залежати від авторизації, тому я вважаю, що перші два рішення на 100% добре, а інші - добре, якщо служба використовує інформацію лише для вирішення, чи дозволяє запит чи ні. Тому я думаю, що дозволи користувача повинні впливати на представлення поточного ресурсу.
inf3rno

1
Я створіть питання про залежність представлень про дозволи. Я продовжую цю відповідь, як тільки отримаю належне рішення.
inf3rno

3
@ inf3rno, це правда, що повністю RESTful сервіс не може залежати від файлів cookie сеансу для автентифікації таким чином, як це традиційно реалізовано. Однак ви можете використовувати файли cookie для аутентифікації, якщо файл cookie містить всю інформацію про стан, якій сервер пізніше знадобиться. Ви також можете захистити файл cookie від підробки, підписавши його за допомогою пари відкритих / приватних ключів. Дивіться мої коментарі нижче.
jcoffland

3
Я не розумію, чому всі, здається, приймають коментар, вам слід зберігати паролі на стороні клієнта та надсилати їх із кожним запитом. Це дуже погана практика та загрожує вашим клієнтам чутливих даних. Ненакопичений пароль (який повинен був бути надсилати його знову і знову) ніколи не повинен зберігатися ніде. Якщо ми приймаємо це, то ви використовуєте жетони, як це робить більшість систем аутентифікації, і тоді будь-який механізм, який ми використовуємо для масштабування сховища жетонів, матиме здебільшого однакові питання щодо масштабованості, як і будь-яка масштабованість сеансу.
lvoelk

334

Перш за все, REST не є релігією і не слід підходити до неї. Хоча RESTful послуги мають переваги, слід дотримуватися принципів REST лише настільки, наскільки вони мають сенс для вашої програми.

Однак, автентифікація та стан клієнта не порушують принципів REST. Хоча REST вимагає, щоб переходи стану були без стану, це стосується самого сервера. В основі всього REST - це документи. Ідея без громадянства полягає в тому, що СЕРВЕР є особою без громадянства, а не клієнтами. Будь-який клієнт, який видав ідентичний запит (ті самі заголовки, файли cookie, URI тощо), повинен бути переведений на те саме місце в програмі. Якщо веб-сайт зберігав поточне місцезнаходження користувача та керував навігацією шляхом оновлення цієї навігаційної змінної на стороні сервера, REST було б порушено. Інший клієнт з однаковою інформацією про запит буде перенесений в інше місце в залежності від стану сервера.

Веб-сервіси Google - це фантастичний приклад системи RESTful. Вони вимагають, щоб заголовок аутентифікації з ключем аутентифікації користувача було передано на кожен запит. Це незначно порушує принципи REST, оскільки сервер відстежує стан ключа автентифікації. Стан цього ключа повинен підтримуватися, і він має певну дату / час закінчення, після якого він більше не надає доступ. Однак, як я вже згадував вгорі своєї посади, жертви повинні бути зроблені, щоб програма могла реально працювати. Враховуючи це, маркери аутентифікації повинні зберігатися таким чином, що дозволяє всім можливим клієнтам продовжувати надавати доступ протягом свого дійсного часу. Якщо один сервер керує станом ключа автентифікації до того моменту, що інший сервер, збалансований навантаженням, не може взяти на себе виконання запитів на основі цього ключа, ви почали реально порушувати принципи REST. Служби Google гарантують, що в будь-який час ви можете взяти маркер автентифікації, який ви використовували на своєму телефоні, проти сервера балансу навантаження A, і натиснути сервер B балансу навантаження зі свого робочого столу, і все ж мати доступ до системи та спрямовуватися на ті самі ресурси, якщо запити були однаковими.

Це все зводиться до того, що вам потрібно переконатися, що ваші жетони аутентифікації перевірені щодо сховища резервного зберігання (база даних, кеш-пам'ять тощо), щоб гарантувати збереження якомога більшої кількості властивостей REST.

Я сподіваюся, що все це мало сенс. Ви повинні також перевірити розділ Constraints в Вікіпедії статтю про Representational State Transfer , якщо ви ще не зробили. Це особливо просвітливо щодо того, на чому насправді сперечаються принципи REST та чому.


6
Я б перефразував ваше первинне твердження. Використовуйте REST, лише якщо обмеження REST мають сенс для вашої програми. Ви можете застосувати підмножину цих обмежень, і ви отримаєте підмножину переваг. Однак у цей момент ви створили свій власний архітектурний стиль. Але це не погано, адже насправді саме це і є перші чотири глави дисертації Роя, принциповий дизайн. REST був лише одним із прикладів.
Даррел Міллер

4
@Darrel Досить справедливий момент. Я, чесно кажучи, не впевнений, як Google це робить, але термін дії може бути закодований в маркер аутентифікації. Я вважаю, що моя більша точка все ще стоїть. Є деякі типи штатів, які просто потрібно підтримувати, і поки ви розумієте, чому REST вимагає безгромадянства, ви можете порушити це таким чином, що має сенс, виключаючи безліч наслідків для решти системи та переваг архітектури RESTful .
Джаред Хардінг

7
Оскільки інших аргументів до цього часу не було, я приймаю цю добре письмову відповідь. Я думаю, що важлива частина полягає в тому, що сервер без стану не означає сервер без громадянства , те, що, на мою думку, часто неправильно розуміється або неправильно застосовується. Сервер може (і зазвичай повинен ) мати будь-який стан, якого він хоче, доки він поводиться безсильно .
деге

10
Я так багато чув проповіді, що сесії не є спокійними. Базова автентифікація HTTP - це справжній крок назад, якщо ви намагаєтесь створити веб-додаток.
Бен Терлі

1
@Micah Henning, ви робите помилкове припущення, що для підтвердження маркера аутентифікації серверу потрібна інформація про стан. Ми можемо обгрунтовано припустити, що ви не можете підробити маркер, який був підписаний парою відкритого / приватного ключів, якщо ви не знаєте приватного ключа. Щоб перевірити маркер дійсний, вам потрібно лише відкритий ключ. Я все ще вважаю, що можлива повністю RESTful аутентифікація.
jcoffland

12

Файли cookie не призначені для автентифікації. Навіщо винаходити колесо? HTTP має добре розроблені механізми аутентифікації. Якщо ми використовуємо файли cookie, ми використовуємо HTTP лише як транспортний протокол, тому нам потрібно створити власну систему сигналізації, наприклад, щоб повідомити користувачам, що вони надали неправильну автентифікацію (використання HTTP 401 було б неправильним, оскільки ми, мабуть, не постачання Www-Authenticateклієнту, як вимагає специфікація HTTP :)). Слід також зазначити, що Set-Cookieце лише рекомендація для клієнта. Його вміст може бути збережений або не може бути збережений (наприклад, якщо файли cookie вимкнено), тоді як Authorizationзаголовок надсилається автоматично при кожному запиті.

Ще один момент полягає в тому, що, щоб отримати файл cookie авторизації, ви, ймовірно, хочете спочатку надати свої облікові дані? Якщо так, то чи не було б це БЕЗПЕЧНО? Простий приклад:

  • Ви намагаєтеся GET /aбез печива
  • Ви якось отримуєте запит на авторизацію
  • Ти йдеш і авторизуєшся якось подобається POST /auth
  • Ви отримуєте Set-Cookie
  • Ви намагаєтеся GET /a з печивом. Але чи GET /aведе себе в цьому випадку бездумно?

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


1
Тим часом я також більше підійшов до цієї точки зору. Я думаю, що технічно це не має великої різниці, це лише заголовки HTTP. Це правда, хоча сама поведінка аутентифікації не є RESTful, якщо потрібно ввійти через окрему адресу. Тож файли cookie - лише симптом більшої проблеми із системою аутентифікації.
деге

Це насправді не пояснює той факт, що веб-браузери підтримують лише Authorization: Basicабо Digest. Якщо ви хочете зробити щось більш вдосконалене, ніж основне або дайджест auth (і вам слід) у контексті браузера, то вам знадобиться щось інше, ніж Authorizationзаголовок.
Олівер Чарльзворт

1
Абсолютно - якщо ви робите чистий JS, то в основному все нормально (за винятком, наприклад, Websockets). Але я можу сказати, що аутентифікація на основі API не обов'язково є єдиною увагою в сценарії браузера.
Олівер Чарльворт

5
GET /aбез файлу cookie та з cookie чітко два різні запити, і прийнятно поводитись по-різному.
TRiG

1
Для додавання до @TRiG, ​​дотримуючись цієї логіки, GET /aзаголовок аутентифікації також такий же, як і GET /aбез заголовка аутентифікації, що робить його однаково непридатним для REST. Якщо ви збираєтеся ставитись до одного заголовка http по-іншому, ніж інший, ви хочете вирішити це як мінімум.
Джаспер

7

Власне, RESTfulness стосується лише РЕСУРСІВ, як це вказано універсальним ідентифікатором ресурсу. Тож навіть говорити про речі, такі як заголовки, файли cookie тощо стосовно 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, або переходять з одного стану REST в інший стан REST. Кожен стан може бути повністю зрозумілий представленням, яке він містить, і набором переходів, які він забезпечує, при цьому переходи обмежуються єдиною Система може бути складною діаграмою стану, але кожен користувальницький агент здатний бачити лише один стан за один раз (поточний стаціонарний стан), і таким чином кожен стан є простим і може бути проаналізований незалежно. користувач OTOH може в будь-який час створити власні переходи (наприклад, ввести URL-адресу, вибрати закладку,відкрити редактор тощо). "


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

Наприклад, коли користувач вводить дані на екран графічного інтерфейсу, клієнт відстежує, які поля були введені, а які немає, які необхідні поля відсутні, тощо. Це все КЛІЄНТ-КОНТЕКСТ, і його не слід надсилати та відслідковувати сервером. Що надсилається на сервер - це повний набір полів, які потрібно змінити в ідентифікованому ресурсі (URI), таким чином, щоб перехід цього ресурсу був з одного стану RESTful в інший.

Отже, клієнт відстежує, що робить користувач, і лише надсилає логічно завершені переходи стану на сервер.


3
Я не бачу, як це проливає світло на поставлене питання.
jcoffland

1

Трансакція HTTP, основна автентифікація доступу, не підходить для RBAC, тому що для автентифікації базового доступу кожен раз використовується зашифроване ім'я користувача: пароль для ідентифікації, тоді як те, що потрібно в RBAC - це роль, яку користувач хоче використовувати для конкретного дзвінка. RBAC не перевіряє дозволи на ім’я користувача, але на ролі.

Ви можете обмінятись, щоб об'єднатись так: usernameRole: пароль, але це погана практика, і це також неефективно, тому що, коли у користувача більше ролей, механізм аутентифікації повинен буде перевірити всі ролі в конкатенації, і це кожен дзвінок знову. Це знищило б одне з найбільших технічних переваг RBAC, а саме дуже швидкий тест на авторизацію.

Тож цю проблему неможливо вирішити за допомогою базової автентифікації доступу.

Щоб вирішити цю проблему, необхідне підтримання сеансів, і це, на думку деяких відповідей, суперечить REST.

Саме це мені подобається у відповіді, що REST не слід трактувати як релігію. Наприклад, у складних бізнес-випадках у галузі охорони здоров’я, наприклад, RBAC є абсолютно поширеним та необхідним. І було б шкода, якби їм не дозволили використовувати REST, оскільки всі розробники інструментів REST трактували б REST як релігію.

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

Якщо у когось є інша ідея, я буду рада її почути.



-4
  1. Сесії не є невмирущими
  2. Ви маєте на увазі, що REST-послуга лише для http-використання, або я помилився? Сеанс на основі файлів cookie повинен використовуватися лише для власних (!) Http-сервісів! (Можливо, буде проблема з файлами cookie, наприклад, з мобільного / консолі / робочого столу тощо).
  3. якщо ви надаєте послугу RESTful для розробників 3d-учасників, ніколи не використовуйте сеанси на основі файлів cookie, а не використовуйте маркери, щоб уникнути проблем із безпекою.

3
файл cookie не повинен використовуватися для зберігання сеансового ключа для сеансу на сервері, який містить маркер аутентифікації. але якщо файл cookie містить маркер автентифікації, це можливе рішення. (звичайно, печиво має бути httpsly та захищеним)
roberkules
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.