Адаптація Web-додатків до мобільних пристроїв без зміни коду

  1. Web-додатки: трохи історії
  2. Web-додаток як чорний ящик
  3. Послідовність входу в систему
  4. лістинг 1
  5. лістинг 2
  6. лістинг 3
  7. лістинг 4
  8. Малюнок 1. Chrome Developer Tools
  9. лістинг 5
  10. лістинг 6
  11. тестування послідовності
  12. лістинг 7
  13. лістинг 8
  14. лістинг 9
  15. лістинг 10
  16. лістинг 11
  17. лістинг 12
  18. Резюме по послідовності входу
  19. Таблиця 1
  20. лістинг 13
  21. лістінг 14
  22. лістинг 15
  23. лістинг 16
  24. лістинг 17
  25. лістинг 18
  26. лістинг 19
  27. лістинг 20
  28. Висновок
  29. Ресурси для скачування

З кожним новим випуском Android, iOS і інших мобільних операційних систем браузери для мобільних пристроїв стають дедалі потужнішими, але фізичні обмеження - невеликий розмір екрану і нова парадигма сенсорного введення замість миші - залишаються. Хоча деякі смартфони в змозі відобразити Web-сайт, результати часто бувають неоптимальними, а то і взагалі непридатними для використання. Розмір кнопок і інших елементів управління повинен бути таким, щоб ними було легко маніпулювати пальцями, а не покажчиком миші, і нові дії, такі як жести, повинні використовуватися ефективно. Коротше кажучи, необхідні узгоджені зусилля, спрямовані на те, щоб зробити ці сайти не тільки допускають використання мобільних пристроїв, але і підтримують їх.

Зменшений екран і сенсорні елементи управління - основні чинники, що визначають необхідність спрощення та реорганізації Web-інтерфейсу, щоб Web-додаток стало зручним для мобільних пристроїв. Можна створити єдиний призначений для користувача інтерфейс, який в настільних і мобільних браузерах працює по-різному, але цей підхід зазвичай застосовується для того, щоб уникнути будь-яких змін в існуючому Web-додатку. Краще підготувати набір статичних ресурсів (розмітка, таблиці стилів, код JavaScript ™), які забезпечують мобільний доступ до функціональності Web-додатки без необхідності змінювати код на стороні сервера. Існуючий сайт залишається абсолютно недоторканим.

Це дозволяє уникнути часто дорогої необхідності розгортати зміни коду у виробничому середовищі. А також усуває потребу в регресійному тестуванні існуючих функцій, які зазнали змін при додаванні мобільного підтримки. Крім того, так як використовуються одні й ті ж запити і відповіді, досить протестувати додаток на продуктивність, якщо є підозра, що додавання мобільного підтримки значно збільшить трафік. При цьому підході просто додаються статичні файли (.html, .css, .js), які обслуговуються з того ж домену, де розміщується існуюче Web-додаток, і використовуються існуючі служби, які додаток вже надає.

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

Web-додатки: трохи історії

Більшість нетривіальних Web-додатків засноване на понятті модель-уявлення-контролер (MVC), причому контролер розташований на сервері. Сервер підтримує стан сеансу і перенаправляє запити користувача на наступну сторінку, яку Ви хочете використовувати. Кожен запит клієнта зазвичай викликає розмітку цілої сторінки, навіть якщо при послідовних зверненнях змінюється тільки частина цієї сторінки. Для вирішення цієї проблеми і підвищення гнучкості при агрегування сторінок була введена схема портлетів.

Потім на сцену увірвався Ajax і народилася система Web 2.0. Ajax дозволив програмувати асинхронні запити. Корисна інформація більше не потребувала розмітці, а могла бути представлена ​​в форматі XML, а пізніше і JSON. Контролер перемістився з серверної частини в клієнтську, а завдання агрегування сторінок переклали на все більш потужні і стандартизовані клієнтські браузери. Отже, на одному кінці спектра є Web-додаток, де браузер мало відрізняється від простого визуализатора розмітки, і всіма функціями і потоками управляє сервер. А на іншому - таке Web-додаток, в якому велика частина логіки управління і потоків виконується в клієнтському браузері (на базі JavaScript, Flash або Silverlight), а сервер забезпечує служби RESTful для надання даних і виконання будь-яких обчислень, які можуть знадобитися з додатком.

Коли потрібні мобільні Web-додатки, на думку зазвичай приходить саме цей останній шаблон, тому що саме так працюють мобільні схеми на кшталт jQuery Mobile або Dojo Mobile. Але що відбувається, коли програму, яку потрібно зробити мобільним, має дизайн, більше орієнтований на сервер, позбавлений REST-служб?

Web-додаток як чорний ящик

Оскільки мета полягає в тому, щоб уникнути будь-яких змін коду програми, його можна розглядати як чорний ящик і зосередитися тільки на тому, що виходить на виході при заданому стані входу; під входом тут розуміються HTTP-запити, а під виходом - до HTTP-відповіді. Таким чином, при створенні мобільного клієнта, що імітує певний HTTP-запит і здатного аналізувати і виявляти всі можливі відповіді на цей запит, з'являється можливість створити мобільну "обкладинку", яка використовує ті ж моделі запит / відповідь, що і існуючу програму. У теорії це виглядає досить просто, але на практиці все може виявитися складніше.

Послідовність входу в систему

Розглянемо приклад послідовності перевірки автентичності користувача типового Web-додатки. (Відомості про HTTP-запити і відповідях можна зібрати за допомогою такого інструменту, як Firefox Firebug або Developer Tools в Chrome або Safari.) Почнемо аутентифікацію з моменту передачі облікових даних. У лістингу 1 показана вихідна розмітка форми входу на сайт.

лістинг 1
<Form name = "logonForm" action = "/ abc / salesportal / j_security_check" method = "post"> <input type = "hidden" name = "page" value = "signin.wss" /> <table cellspacing = "0 "cellpadding =" 0 "class =" sign-in-table "> <tr> <td class =" c1 "> <label for =" user_id "> Email ID: </ label> </ td> <td> < input dir = "ltr" class = "wpsEditField" size = "30" value = "" name = "j_username" id = "user_id" type = "text" /> (eg, [email protected]) </ td> </ tr> <tr> <td class = "c1"> <label for = "passwd"> Password: </ label> </ td> <td> <input dir = "ltr" class = "wpsEditField" size = "30" id = "passwd" name = "j_password" type = "password" /> </ td> </ tr> <tr> <td> <input type = "hidden" name = "login-form-type" value = "pwd" /> </ td> </ tr> <tr> <td> & nbsp; </ td> <td> <p> <a href = "http://www.abc.com/password" > Forgot your password? </a> </ p> </ td> </ tr> </ table> <table cellspacing = "0" cellpadding = "0" class = "submit-table"> <tr> <td class = "buttons" align = "right"> <span class = "button-blue"> <input type = "submit" value = "Submit" name = "submitButton" /> </ span> <span class = "button-blue"> < input type = "button" value = "Cancel" name = "cancelButton" onclick = "cancelSignIn ()" /> </ span> </ td> </ tr> </ table> </ form>

У наведеному вище коді, коли користувач вводить своє ім'я, пароль і натискає кнопку "Відправити", облікові дані передаються в j_security_check. Досить просто. Давайте симітіруем запит перевірки автентичності.

Дуже корисний інструмент для тестування Web-додатки як чорного ящика - cURL, який дозволяє будувати і простежувати HTTP-взаємодії (див. Розділ ресурси ). Він надає повний контроль, дозволяючи будувати кожен аспект запиту (лістинг 2). Крім того, не потрібно турбуватися про те, що у різних браузерів може бути різний кеш.

лістинг 2
curl --verbose --silent --show-error -d "page = signin.wss" --data-urlencode "[email protected]" --data-urlencode "j_password = my + passw0rd" - data-urlencode "submit = Submit" http://www.abc.com/abc/salesportal/j_security_check

Зверніть увагу, що в цій команді cURL все поля введення користувача мають URL, закодований за допомогою --data-urlencode. Вона також автоматично привласнює application / x-www-form-urlencoded заголовок content-type. Крім того, використання прапора --data * означає, що це буде запит POST. У cURL існує багато прапорів, і можна відтворити будь-який необхідний запит.

На жаль, як видно з лістингу 3, ця проста форма POST не дає очікуваного результату.

лістинг 3
* About to connect () to www.abc.com port 80 (# 0) * Trying 192.168.1.1 ... connected * Connected to www.abc.com (192.168.1.1) port 80 (# 0)> POST / abc / salesportal / j_security_check HTTP / 1.1> User-Agent: curl / 7.21.3 (i386-pc-win32) libcurl / 7.21.3 OpenSSL / 0.9.8q zlib / 1.2.5> Host: www.abc.com> Accept: * / *> Content-Length: 91> Content-Type: application / x-www-form-urlencoded> <HTTP / 1.1 302 Found <Date: Fri, 18 Nov 2011 16:57:39 GMT <Server: IBM_HTTP_Server / 6.1 .0.27 Apache / 2.0.47 <Location: http://www.abc.com/abc/salesportal/ <Expires: Thu, 01 Dec 1994 16:00:00 GMT <Cache-Control: no-cache = "set- cookie, set-cookie2 "<Content-Type: www / unknown <Content-Language: en-US <Content-Length: 0 <Proxy-Connection: Keep-Alive <Connection: Keep-Alive <Set-Cookie: JSESSIONID = 0001KBumJR -0VZ6ybqAm1WIOn-N: 15lgjrufm; Path = / <Set-Cookie: LtpaToken2 = g2VF5mHGQCmv73URFPz ... zmsD5ifgdfSiJghQA / jJdUoABkaik =; Path = /; Domain = .abc.com <Set-Cookie: LtpaToken = 3UpRmGDYVqd + 1ZdKm03v ... DSyZhJz4wpt + BFeBXTGn0Ww ==; Path = /; Domain = .abc.com <* Connection # 0 to host www.abc.com left intact * Closing connection # 0

Присутність файлів cookie LTPA вказує на те, що перевірка справжності, схоже, виконана, але запит перенаправлений за адресою / abc / salesportal /.

Якщо додати в cURL-запит прапор --location, то перенаправлення буде автоматично відслідковуватися. При наступній спробі в лістингу 4 ми бачимо початковий POST і редирект 302 (як і вище), а також наступні запит і відповідь.

лістинг 4
* Connection # 0 to host www.abc.com left intact * Issue another request to this URL: 'http://www.abc.com/abc/salesportal/' * Violate RFC 2616 / 10.3.3 and switch from POST to GET * Re-using existing connection! (# 0) with host www.abc.com * Connected to www.abc.com (192.168.1.1) port 80 (# 0)> GET / abc / salesportal / HTTP / 1.1> User-Agent: curl / 7.21.3 (i386-pc-win32) libcurl / 7.21.3 OpenSSL / 0.9.8q zlib / 1.2.5> Host: www.abc.com> Accept: * / *> <HTTP / 1.1 200 OK <Date: Fri, 18 Nov 2011 16:39:04 GMT <Server: IBM_HTTP_Server / 7.0.0.15 <Cache-Control: max-age = 300 <Expires: Fri, 18 Nov 2011 16:44:04 GMT <Content-Type: text / html; charset = ISO-8859-1 <Content-Length: 820 <Proxy-Connection: Keep-Alive <Connection: Keep-Alive <Age: одна тисяча п'ятсот сорок п'ять <<! DOCTYPE HTML PUBLIC "- // W3C // DTD HTML 3.2 Final // EN" > <html> <head> <title> Index of / abc / salesportal </ title> </ head> <body> <h1> Index of / abc / salesportal </ h1> <ul> <li> <a href = "/ abc /"> Parent Directory </a> </ li> <li> <a href="ccr/"> ccr / </a> </ li> <li> <a href="downloads/"> downloads / </a> </ li> <li> <a href="home2/"> home2 / </a> </ li> <li> <a href="html/"> html / </a> </ li> <li> <a href="images/"> images / </a> </ li> <li> <a href="js/"> js / </a> </ li> <li > <a href="media/"> media / </ a > </ Li> <li> <a href="regs/"> regs / </a> </ li> <li> <a href="style/"> style / </a> </ li> < / ul> </ body> </ html> * Connection # 0 to host www.abc.com left intact * Closing connection # 0

Зверніть увагу, що хоча і здається, що перевірка справжності пройшла успішно, результуюча сторінка - це всього лише список каталогів. Це не те, чого ми очікували. Розглянемо потік аутентифікації докладніше. Для цього відкриємо Chrome і скористаємося його інструментарієм Developer Tools для перегляду деталей мережевих з'єднань в процесі успішного входу (рисунок 1).

Малюнок 1. Chrome Developer Tools

У лістингу 5 можна помітити, що запиті передаються повноваження, повертає деякі куки, і потім виконує перенаправлення. Придивіться до заголовків запиту. Передаються не тільки дані форми (які можуть мати приховані поля), але і куки.

Кукі AbcSurvey і UnicaNOIDID виглядають нешкідливими, це просто обстеження і аналіз сайту. Кукі JSESSIONID відноситься до сеансу на стороні сервера (Java ™), який, ймовірно, не містить нічого цінного, чого не можна легко відтворити, так як ми щойно прибули на Web-сайт і фактично нічого не зробили. Однак є ще один, особливий куки-файл: WASReqURL.

лістинг 5
Request URL: https://www.abc.com/abc/salesportal/j_security_check Request Method: POST Status Code: 302 Found Request Headers Accept: text / html, application / xhtml + xml, application / xml; q = 0.9, * /*;q=0.8 Accept-Charset: ISO-8859-1, utf-8; q = 0.7, *; q = 0.3 Accept-Encoding: gzip, deflate, sdch Accept-Language: en-US, en; q = 0.8 Cache-Control: max-age = 0 Connection: keep-alive Content-Length: 105 Content-Type: application / x-www-form-urlencoded Cookie: abcSurvey = 1321469743354; UnicaNIODID = h8DDq7LCdW1-XQbeXFj; JSESSIONID = 0001nxuprbDjqGuXSM97y2kwxeq: 15bfc0945; WASReqURL = https: ///abc/salesportal/signin.wss; Host: www.abc.com Origin: https://www.abc.com Referer: https://www.abc.com/abc/salesportal/registration/signIn.jsp User-Agent: Mozilla / 5.0 (Windows NT 6.1 ; WOW64) AppleWebKit / 535.2 + (KHTML, like Gecko) Chrome / 15.0.874.106 Safari / 535.2 Form Data page: signin.wss j_username: [email protected] j_password: my + passw0rd login-form-type: pwd submitButton: Submit Response Headers Cache-Control: no-cache = "set-cookie, set-cookie2" Connection: Keep-Alive Content-Language: en-US Content-Length: 0 Content-Type: www / unknown Date: Thu, 17 Nov 2011 19:08:17 GMT Expires: Thu, 01 Dec одна тисяча дев'ятсот дев'яносто чотири 16:00:00 GMT Location: https: //www.abc.com/abc/salesportal/signin.wss Server: IBM_HTTP_Server / 6.1.0.37-PM46234 Apache / 2.0 .47 Set-Cookie: WASReqURL = ""; Expires = Thu, 01 Dec 1994 16:00:00 GMT; Path = / LtpaToken2 = jxSSeGottXC2WQZhQFeXf ... PP1u + D / 8PFtGPQs0sjiYanjdBJEfF0Zj6kXt1QeE =; Path = / LtpaToken = bTzeMj + SSZFQMUKSZ20tc1 ... qPSY0kR5MFrKrqZDOwd7twfpupFNUpZUKlEGE41c =; Path = /

Схоже, що на адресу перенаправлення впливає саме присутність WASReqURL.

Потім відповідь видаляє WASReqURL, роблячи вичерпаним термін його дії. Ще він відправляє два нових куки-файлу: LtpaToken і LtpaToken2, які повертаються лише після успішного входу в систему. URL-адресу перенаправлення міститься в заголовку Location. Далі слід запит, наведений у лістингу 6. ​​Зверніть увагу, що в запит направляються новоспечені куки LTPA, так як WASReqURL, фактично, був знищений. Тепер Web-додаток розглядає цей запит до /signin.wss як аутентіфіцированний запит. У відповідь на цей другий запит повертаються два нових куки-файлу: ODC_CG і ODC_REG.

лістинг 6
Request URL: https: //www.abc.com/abc/salesportal/signin.wss Request Method: GET Status Code: 200 OK Request Headers Accept: text / html, application / xhtml + xml, application / xml; q = 0.9 , * / *; q = 0.8 Accept-Charset: ISO-8859-1, utf-8; q = 0.7, *; q = 0.3 Accept-Encoding: gzip, deflate, sdch Accept-Language: en-US, en; q = 0.8 Cache-Control: max-age = 0 Connection: keep-alive Cookie: abcSurvey = 1321469743354; UnicaNIODID = h8DDq7LCdW1-XQbeXFj; JSESSIONID = 0001nxuprbDjqGuXSM97y2kwxeq: 15bfc0945; LtpaToken2 = jxSSeGottXC2WQZh ... anjdBJEfF0Zj6kXt1QeE =; LtpaToken = bTzeMj + SSZFQMUKSZ ... twfpupFNUpZUKlEGE41c = Host: www.abc.com Referer: https: //www.abc.com/abc/salesportal/registration/signIn.jsp User-Agent: Mozilla / 5.0 (Windows NT 6.1; WOW64) AppleWebKit / 535.2 (KHTML, like Gecko) Chrome / 15.0.874.106 Safari / 535.2 Response Headers Cache-Control: no-cache = "set-cookie, set-cookie2" Connection: Keep-Alive Content-Encoding: gzip Content- Language: en-US Content-Type: text / html; charset = ISO-8859-1 Date: Thu, 17 Nov 2011 19:08:20 GMT Expires: Thu, 01 Dec 1994 16:00:00 GMT Server: IBM_HTTP_Server / 6.1.0.37-PM46234 Apache / 2.0.47 Set-Cookie: ODC_CG = 123456; Domain = abc.com ODC_REG = Joe Park|[email protected]; Expires = Fri, 16 Nov 2012 19:08:19 GMT; Path = /; Domain = abc.com Transfer-Encoding: chunked

Корисна інформація цього другого запиту містить всю розмітку для відображення домашньої сторінки Web-додатки після аутентифікації (не показана).

тестування послідовності

Перед початком написання коду корисно створити автономний запит для перевірки ступеня нашого розуміння потоку запит / відповідь. За допомогою інформації, що надається засобами відстеження з інструментів розробки Chrome, можна побудувати новий запит входу cURL (лістинг 7).

лістинг 7
curl --verbose --silent --show-error -d "page = signin.wss" --data-urlencode "[email protected]" --data-urlencode "j_password = my + passw0rd" - data-urlencode "submit = Submit" --cookie "WASReqURL = http: ///abc/salesportal/signin.wss" --cookie-jar login-cookies.txt http://www.abc.com/abc/salesportal / j_security_check

Без збереження куки cURL не поводиться належним чином. Отримавши заголовок Set-Cookie для WASReqURL, який вказує, що термін куки-файлу закінчився, cURL ігнорує це і в подальшому запиті перенаправлення продовжує пересилати cookie (без змін). Щоб виправити це, включіть режим збереження куки за допомогою --cookie jar. Тепер будь-які зміни куки-файлу в редирект 302 передаються в наступний запит.

Зверніть увагу, що до запиту доданий куки-файл WASReqURL. Необхідно також мати можливість записувати куки cURL (--cookie jar, см. Врізку), так як в процесі перенаправлення термін дії WASReqURL закінчується.

Вихід cURL показаний в лістингу 8.

лістинг 8
* About to connect () to www.abc.com port 80 (# 0) * Trying 192.168.1.1 ... connected * Connected to www.abc.com (192.168.1.1) port 80 (# 0)> POST / abc / salesportal / j_security_check HTTP / 1.1> User-Agent: curl / 7.21.3 (i386-pc-win32) libcurl / 7.21.3 OpenSSL / 0.9.8q zlib / 1.2.5> Host: www.abc.com> Cookie: WASReqURL = http: ///abc/salesportal/signin.wss> Accept: text / html, application / xhtml + xml, application / xml; q = 0.9, * / *; q = 0.8> Content-Length: 91> Content -Type: application / x-www-form-urlencoded

POST-запит виробляється за допомогою куки WASReqURL (лістинг 9).

лістинг 9
<HTTP / 1.1 302 Found <Date: Fri, 18 Nov 2011 19:40:56 GMT <Server: IBM_HTTP_Server / 6.1.0.27 Apache / 2.0.47 <Location: http://www.abc.com/abc/salesportal/ signin.wss <Expires: Thu, 01 Dec тисяча дев'ятсот дев'яносто чотири 16:00:00 GMT <Cache-Control: no-cache = "set-cookie, set-cookie2" <Content-Type: www / unknown <Content-Language: en- US <Content-Length: 0 <Proxy-Connection: Keep-Alive <Connection: Keep-Alive * Added cookie JSESSIONID = "0001iuuXHCo5zHq5oTRRZLDYy4B: 15lgjrufm" for domain www.abc.com, path /, expire 0 <Set-Cookie: JSESSIONID = 0001iuuXHCo5zHq5oTRRZLDYy4B: 15lgjrufm; Path = / * Added cookie WASReqURL = "" "" for domain www.abc.com, path /, expire 786297600 <Set-Cookie: WASReqURL = ""; Expires = Thu, 01 Dec 1994 16:00:00 GMT; Path = / * Added cookie LtpaToken2 = "gsHvV95vwKOU8D1CBzy5u1 ... z6WI0RUTpn9RJLc6OVc4LlFvh2UnanTo0 =" for domain abc.com, path /, expire 0 <Set-Cookie: LtpaToken2 = gsHvV95vwKOU8D1CBzy5u1 ... z6WI0RUTpn9RJLc6OVc4LlFvh2UnanTo0 =; Path = /; Domain = .abc.com * Added cookie LtpaToken = "3UpRmGDYVqd + 1Zd788 + 5x7xr8Km03vwUqWkw4 ... wpt + BFSht7DYg / deBXTGn0Ww ==" for domain abc.com, path /, expire 0 <Set-Cookie: LtpaToken = 3UpRmGDYVqd + 1Zd788 + 5x7xr8Km03vwUqWkw4 ... wpt + BFSht7DYg / deBXTGn0Ww ==; Path = /; Domain = .abc.com

Ми отримуємо редирект 302 за адресою /signin.wss. Термін дії куки WASReqURL закінчився, і так як облікові дані дійсні, повертаються два куки LTPA (лістинг 10).

лістинг 10
* Connection # 0 to host www.abc.com left intact * Issue another request to this URL: 'http://www.abc.com/abc/salesportal/signin.wss' * Violate RFC 2616 / 10.3.3 and switch from POST to GET * Re-using existing connection! (# 0) with host www.abc.com * Connected to www.abc.com (192.168.1.1) port 80 (# 0)> GET /abc/salesportal/signin.wss HTTP / 1.1> User-Agent: curl / 7.21.3 (i386-pc-win32) libcurl / 7.21.3 OpenSSL / 0.9.8q zlib / 1.2.5> Host: www.abc.com> Cookie: LtpaToken2 = gsHvV95vwKOU8D1CBzy5u1 ... z6WI0RUTpn9RJLc6OVc4LlFvh2UnanTo0 =; JSESSIONID = 0001iuuXHCo5zHq5oTRRZLDYy4B: 15lgjrufm; LtpaToken = 3UpRmGDYVqd + 1Zd788 + 5x7xr8Km03vwUqWkw4 ... wpt + BFSht7DYg / deBXTGn0Ww ==; WASReqURL = http: /// abc / salesportal / signin.wss> Accept: text / html, application / xhtml + xml, application / xml; q = 0.9, * / *; q = 0.8

Відбувається перенаправлення за вказаною URL (/signin.wss), і передаються куки LTPA і JSESSIONID. Зауважимо, що cURL помилково вказує, що куки-файл WASReqURL переслати, незважаючи на те, що термін його дії, відповідно до попередньої відповіді, вже закінчився. Однак так як ми дозволили зберігати куки в cURL (--cookie jar), куки-файл все ж не був відправлений.

Вміст файлу cookie-jar показано в лістингу 11.

лістинг 11
# Netscape HTTP Cookie File # http://curl.haxx.se/rfc/cookie_spec.html # This file was generated by libcurl! Edit at your own risk. www.abc.com FALSE / FALSE 0 JSESSIONID 0001iuu ... RRZLDYy4B: 15lgjrufm www.abc.com FALSE / FALSE 786297600 WASReqURL "" .abc.com TRUE / FALSE 0 LtpaToken2 gsHvV8D1CBzy5 ... I0RUTpn2UnanTo0 = .abc.com TRUE / FALSE 0 LtpaToken 3s1Zd88 + 5x ... hJz4wpt + BXTGn0Ww == .abc.com TRUE / abc / salesportal / FALSE 0 ODC_CG "" .abc.com TRUE / FALSE 1353181257 ODC_REG Joe Smith|[email protected]

Запит GET за адресою /signin.wss завершується успішно. Він повертає ще два куки: ODC_CG і ODC_REG (лістинг 12).

лістинг 12
> HTTP / 1.1 200 OK> Date: Fri, 18 Nov 2011 19:40:57 GMT> Server: IBM_HTTP_Server / 6.1.0.27 Apache / 2.0.47> Expires: Thu, 01 Dec +1994 16:00:00 GMT> Cache- Control: no-cache = "set-cookie, set-cookie2"> Content-Type: text / html; charset = ISO-8859-1> Content-Language: en-US> Transfer-Encoding: chunked> Proxy-Connection: Keep-Alive> Connection: Keep-Alive * Added cookie ODC_CG = "" "" for domain abc.com, path / abc / salesportal /, expire 0> Set-Cookie: ODC_CG = ""; Domain = abc.com * Added cookie ODC_REG = "Joe Smith|[email protected]" for domain abc.com, path /, expire 1353181257> Set-Cookie: ODC_REG = Joe Smith|[email protected]; Expires = Sat, 17 Nov 2012 19:40:57 GMT; Path = /; Domain = abc.com> * Connection # 0 to host www.abc.com left intact * Closing connection # 0

Розмітка, повернута в цьому останньому відповіді (не показана), відповідає домашній сторінці після аутентифікації, так що все вхідні дані (як і перенаправлення минулих куки) привели до успішного входу.

Резюме по послідовності входу

У всій цій послідовності можна виділити вхідні повідомлення аутентифікації (таблиця 1) і відповіді після успішного входу (таблиця 2).

Таблиця 1
Тип вхідного ПОВІДОМЛЕННЯ Зміст Кукі abcSurvey = 1321469743354; <<< UNIMPORTANT
UnicaNIODID = h8DDq7LCdW1-XQbeXFj; <<< UNIMPORTANT
JSESSIONID = 0001nxuprbDjqGuXSM97y2kwxeq: 15bfc0945; <<< UNIMPORTANT
WASReqURL = https: ///abc/salesportal/signin.wss; Форма page: signin.wss <<< HIDDEN
j_username: [email protected]
j_password: my + passw0rd
login-form-type: pwd <<< HIDDEN
submitButton: Submit <<< UNIMPORTANT дані форми кодуються як Content-Type: application / x-www-form-urlencoded
Таблиця 2
Тип віхідного ПОВІДОМЛЕННЯ Зміст Кукі WASReqURL = https: ///abc/salesportal/signin.wss; <<< CREATED then DESTROYED
LtpaToken2 = jxSSeGottXC2WQZhQFeXfGHapYGLTzXDDvLf ... cl8PFtGPQs0sjiYanjdBJEfF0Zj6kXt1QeE =; Path = /
LtpaToken = bTzeMj + SSZFQMUKSZ20tc1efTrTZodEkEkqPt ... nR5MFrKrqZDOwd7twfpupFNUpZUKlEGE41c =; Path = /
ODC_CG = 123456; Domain = abc.com
ODC_REG = Joe Smith|[email protected]; Expires = Fri, 16 Nov 2012 19:08:19 GMT; Path = /; Domain = abc.com

Розмітка домашньої сторінки після аутентифікації показана в лістингу 13.

лістинг 13
<! DOCTYPE html PUBLIC "- // W3C // DTD XHTML 1.0 Transitional // EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html lang = "en -US "> <head> <meta http-equiv =" content-type "content =" text / html; charset = iso-8859-1 "/> <meta name =" description "content =" Registered Home Page Salesportal " /> <meta name = "keywords" scheme = "iso8601" content = "ABC, Salesportal, sellers, home, registered, signed in" /> ...

Таким чином, щоб створити сторінку входу для мобільних пристроїв, потрібно мати можливість відправляти ті ж повідомлення. Повідомлення типу форми - не проблема; проблеми створюють куки, а саме, WASReqURL.

Сторінка входу для мобільних пристроїв стане частиною нового статичного файлу - "мобільного обкладинки", яка згадувалася вище. Назвемо його mobile.html. Він буде обслуговуватися з того ж сервера, що і існуючу програму, тому не завадить політика безпеки Single Origin security Policy (SOP). Коли користувач відправляє запит на перевірку справжності з мобільного пристрою, він передається за допомогою XMLHttpRequest (XHR) який, як відомо, повторює запит HTTP, але виконується за лаштунками. Однак ці запити XHR або Ajax не ідентичні звичайному запитом HTTP. У програмному XHR-запиті не можна просто поставити заголовок Cookie (як ми робили за допомогою утиліти cURL). согласно специфікації W3 для XMLHttpRequest , Цей конкретний заголовок заборонений.

Крім того, створення кукі WASReqURL на стороні клієнта не призведе до його включенню в таку відповідь сервера, тому що домен cookie може бути заданий тільки на стороні сервера. Єдиний спосіб включити цей куки в запит - спочатку отримати його від сервера. Для цього потрібно знати, коли був створений куки WASReqURL. Запит сторінки входу показаний в лістингу 14.

лістінг 14
Request URL: https: //www.abc.com/abc/salesportal/signin.wss Request Method: GET Status Code: 302 Found Request Headers Accept: text / html, application / xhtml + xml, application / xml; q = 0.9 , * / *; q = 0.8 Accept-Charset: ISO-8859-1, utf-8; q = 0.7, *; q = 0.3 Accept-Encoding: gzip, deflate, sdch Accept-Language: en-US, en; q = 0.8 Connection: keep-alive Cookie: abcSurvey = 1321469743354; UnicaNIODID = h8DDq7LCdW1-XQbeXFj; JSESSIONID = 0001nxuprbDjqGuXSM97y2kwxeq: 15bfc0945 Host: www.abc.com Referer: https://www.abc.com/abc/salesportal/home.wss User-Agent: Mozilla / 5.0 (Windows NT 6.1; WOW64) AppleWebKit / 535.2 (KHTML , like Gecko) Chrome / 15.0.874.106 Safari / 535.2 Response Headers Cache-Control: no-cache = "set-cookie, set-cookie2" Connection: Keep-Alive Content-Language: en-US Content-Length: 0 Content- Type: www / unknown Date: Thu, 17 Nov 2011 18:50:02 GMT Expires: Thu, 01 Dec 1994 16:00:00 GMT Location: https://www.abc.com/abc/salesportal/registration/signIn .jsp Server: IBM_HTTP_Server / 6.1.0.37-PM46234 Apache / 2.0.47 Set-Cookie: WASReqURL = https: ///abc/salesportal/signin.wss; Path = /

Зверніть увагу, що цей початковий запит сторінки signin.wss перенаправляється на /registration/signIn.jsp. Ще важливіше те, що у відповіді повертається куки WASReqURL. Цей кукі відправляється в другому запиті (лістинг 15). (Помилка в Safari 5.1.1 для Windows перешкоджає перенаправлення куки, створених у відповіді 302, в повторних запитів GET. Але в Safari для iOS це працює.)

лістинг 15
Request URL: https: //www.abc.com/abc/salesportal/registration/signIn.jsp Request Method: GET Status Code: 200 OK Request Headers Accept: text / html, application / xhtml + xml, application / xml; q = 0.9, * / *; q = 0.8 Accept-Charset: ISO-8859-1, utf-8; q = 0.7, *; q = 0.3 Accept-Encoding: gzip, deflate, sdch Accept-Language: en-US, en; q = 0.8 Connection: keep-alive Cookie: abcSurvey = 1321469743354; UnicaNIODID = h8DDq7LCdW1-XQbeXFj; JSESSIONID = 0001nxuprbDjqGuXSM97y2kwxeq: 15bfc0945; WASReqURL = https: /// abc / salesportal / signin.wss Host: www.abc.com Referer: https: //www.abc.com/abc/salesportal/home.wss User-Agent: Mozilla / 5.0 (Windows NT 6.1; WOW64) AppleWebKit / 535.2 (KHTML, like Gecko) Chrome / 15.0.874.106 Safari / 535.2 Response Headers Connection: Keep-Alive Content-Encoding: gzip Content-Language: en-US Content-Type: text / html; charset = ISO-8859-1 Date: Thu, 17 Nov 2011 18:50:03 GMT Server: IBM_HTTP_Server / 6.1.0.37-PM46234 Apache / 2.0.47 Transfer-Encoding: chunked

Відтворення послідовності входу за допомогою Ajax

В ході цього розслідування ми з'ясували, що мобільний клієнт повинен виконати запит GET за адресою /signin.wss з єдиною метою отримання куки WASReqURL перед відправкою форми для входу.

При використанні jQuery цей запит буде виглядати як в лістингу 16.

лістинг 16
$ .Ajax ({type: 'GET', url: '/abc/salesportal/signin.wss', async: false, complete: function (xhr, textStatus) {if (xhr.readyState == 4) {if (xhr .status == 200) {console.log (document.cookie);}}}});

В консолі JavaScript ви повинні побачити WASReqURL = http: ///abc/salesportal/signin.wss (серед інших куки).

Коли куки WASReqURL присутній на стороні клієнта, можна створити запит POST для відправки облікових даних на сервер.

Як тільки користувач представив свої облікові дані, в захоплену послідовність включається POST за адресою / abc / salesportal / j_security_check, що призводить до перенаправлення 302 за адресою /abc/salesportal/signin.wss. HTTP також перенаправляє результат в інший метод, в даному випадку, з POST в GET. Будь-які куки, повернуті в першому запиті, будуть перенаправлені в другій запит (якщо це дозволено в Path для куки). Все це відбувається прозоро в запиті Ajax. Програмно ні редирект 302, ні проміжну відповідь і (повторний) запит побачити не можна (лістинг 17).

лістинг 17
var params = 'j_username =' + encodeURIComponent ($ ( '# user_id'). val ()) + '& j_password =' ​​+ encodeURIComponent ($ ( '# passwd'). val ()) + '& page = signin.wss & submit = Submit '; $ .Ajax ({type: 'POST', url: '/ abc / salesportal / j_security_check', contentType: 'application / x-www-form-urlencoded', data: params, cache: false, // Не можна задати заголовок Cookie - заборонено // headers: { 'Cookie': 'WASReqURL = http: ///abc/salesportal/signin.wss'}, // Ще одна спроба поставити заголовок Cookie: // beforeSend: function (xhr) {xhr.setRequestHeader ( "Cookie", "WASReqURL = http: ///abc/salesportal/signin.wss");}, complete: function (xhr, textStatus) {вР}});

Не забудьте зашифрувати інформацію, що вводиться користувачем в форму, за допомогою encodeURIComponent (), коли дані типу application / x-www-form-urlencoded передаються в запиті POST.

Відправивши запит аутентифікації, необхідно якось дізнатися, що перевірка пройшла успішно.

Проблема в тому, що в обох випадках запит буде (прозоро) перенаправлений на нову сторінку. При підході RESTful ви просто отримали б код відповіді 2xx в разі успіху або 401 при спробі несанкціонованого доступу. Або ж ви могли б отримати спеціальний відповідь, упакований в корисні дані JSON. Однак для цього конкретного Web-додатки ці зручні форми відповіді недоступні, тому нам доведеться працювати з поточною моделлю "запит-відповідь":

  • успішний вхід буде перенаправлений на адресу /abc/salesportal/signin.wss;
  • невдала спроба входу буде перенаправлено на адресу /abc/salesportal/registration/signInError.jsp;
  • Остаточний код стану HTTP в обох випадках буде 200 (ОК).

Прозоре перенаправлення запитів Ajax проблематично, тому що клієнт не отримає ніякого повідомлення про переадресації або кінцевого URL (заголовок Location редиректу 302 втрачається).

Об'єкт JavaScript XHR не надає доступ до запиту, а це означає, що кінцевий URL запиту невідомий. Все, що є для визначення результату обробки запиту, - це аналіз заголовків або корисних даних відповіді (на останній запит).

У лістингу 17 це робиться в функції complete ().

Для спрощення синтаксичного аналізу розмітки відповіді перетворимо відповідь в формат XML (якщо він ще не в ньому), як показано в лістингу 18. (Якщо потрібно обслуговувати не тільки браузери на основі WebKit, то більш надійний спосіб зробити це наведено в розділі ресурси .)

лістинг 18
complete: function (xhr, textStatus) {if (xhr.readyState == 4) {if (xhr.status == 200) {var xmlResponse = xhr.responseXML; if (xhr.responseXML == null) {if (typeof DOMParser! = "undefined") {var parser = new DOMParser (); xmlResponse = parser.parseFromString (xhr.responseText, "text / xml"); }}} / * Else if (xhr.status == 302 || xhr.status == 303) {} // цього ніколи не буде ,, тільки 200 для сторінки signInError else if (xhr.status == 401) // UNAUTHORIZED - цього ніколи не буде, так як додаток перенаправляє запит на сторінку signInError * /}}

Після розбору відповіді DOM наступний крок полягає в однозначної ідентифікації даних зі сторінок signin.wss і signInError.jsp, унікальних для цих сторінок. Вони повинні бути присутніми завжди, і в ідеалі це має бути щось, що не зміниться в разі перетворення сторінки при зміні призначеного для користувача інтерфейсу. Прямі кандидати на цю роль - метатеги. Тут використовуються теги ключових слів, тому що сторінка успішного входу містила ключові слова signed in, а сторінка помилки - failure і error (лістинг 19).

лістинг 19
<! DOCTYPE html PUBLIC "- // W3C // DTD XHTML 1.0 Transitional // EN" "http://www.w3.org/TR/ xhtml1 / DTD / xhtml1-transitional.dtd"> <html lang = "en -US "> <head> <meta http-equiv =" content-type "content =" text / html; charset = iso-8859-1 "/> <meta name =" description "content =" Registered Home Page Salesportal " /> <meta name = "keywords" scheme = "iso8601" content = "ABC, Salesportal, sellers, home, registered, signed in" /> ...

При використанні відповіді DOM, отриманого вище, вельми корисним для вилучення вмісту з метатега ключових слів виявляються селектори jQuery (лістинг 20).

лістинг 20
var isErrorPage = true; var metaKeywordsTag = $ ( 'meta [name = "keywords"]', xmlResponse); if (metaKeywordsTag! = null) {var keywords = metaKeywordsTag.attr ( "content"); if (keywords! = null && ((keywords.indexOf ( "fail")> = 0) || (keywords.indexOf ( "error")> = 0))) {isErrorPage = true; } Else {if (keywords! = Null && (keywords.indexOf ( "signed in")> = 0)) isErrorPage = false; }}

Тепер, коли прапор isErrorPage встановлений, мобільний клієнт зможе відобразити наступну сторінку в потоці сторінок аутентифікації або діалогове вікно помилки входу.

Висновок

Приклад входу, описаний в цій статті, показує, як програмно відтворити ту ж послідовність "запит-відповідь", очікувану Web-додатком, за допомогою XmlHTTPRequest. Розглядаючи додаток як чорний ящик, ми сумлінно відтворили вхідні повідомлення і використовували надійний спосіб інтерпретації вихідних повідомлень, що дозволило уникнути внесення змін до існуючу програму для забезпечення підтримки мобільних клієнтів. Так як код сервера залишається незмінним, немає необхідності виконувати дороге розгортання у виробничому середовищі і проводити великі регресивні тести і тести продуктивності.

В ідеалі Web-сайт слід спочатку розробляти за правилами адаптивного Web-дизайну . Однак реальність така, що багато Web-сайти розроблені без оглядки на мобільні пристрої, і для їх переробки під мобільний Інтернет будуть потрібні значні інвестиції. Пропонований метод являє собою компроміс, що дозволяє швидко пристосувати сайт для мобільного Web.

Ресурси для скачування

Схожі тими

Підпішіть мене на ПОВІДОМЛЕННЯ до коментарів

Але що відбувається, коли програму, яку потрібно зробити мобільним, має дизайн, більше орієнтований на сервер, позбавлений REST-служб?
Com/password" > Forgot your password?

Дополнительная информация

rss
Карта