ru:https://highload.today/blogs/chto-takoe-avtorizatsionnyj-server-i-kak-ego-pravilno-vybrat-gajd-osnovannyj-na-moem-opyte/ ua:https://highload.today/uk/blogs/shho-take-avtorizatsijnij-server-ta-yak-jogo-pravilno-obrati-gajd-zasnovanij-na-moyemu-dosvidi/
logo
Досвід      15/07/2022

Що таке авторизаційний сервер та як його правильно обрати: гайд, заснований на моєму досвіді

Дмитро Богдан BLOG

.NET Solution Architect в NIX

Ця стаття базується на моєму досвіді участі в одному з проєктів.

Для початку пройдемось по теорії — що таке авторизаційний сервер та навіщо він потрібен. Далі спробуємо обрати підходяще рішення. Я розповім про основні користувальницькі флоу авторизації та варіанти їх використання. І насамкінець виділю ключові концепції розробки кастомного авторизаційного сервера та поширені помилки.

Але перед цим — невелика ремарка.

Авторизаційний сервер, айдентіті-сервер, аус-сервер — під цими поняттями зазвичай мають на увазі software, який на основі юзер-логіна та юзер-пароля видає токен або щось подібне для авторизації в системі.

У цій статті ви можете помітити різні терміни, але відмінностей між ними немає.

Нам дістався legacy-моноліт…

Наша історія вибору авторизаційного сервера почалася з роботи над одним із давніх проєктів — legacy-моноліту на .NET 3.5. Авторизація в ньому була виконана у вигляді звичайної сторінки на ASP, де користувач вводить логін та пароль. В code-behind була встановлена сесія, куди записувалась необхідна інформація. Це й була аутентифікація та авторизація користувача.

Флоу дуже простий: система зверталася до бази, порівнювала збережені раніше та введені зараз дані і повертала дозвіл або заборону на доступ. При цьому програма ні з чим не інтегрується.

Однак згодом ми зіткнулися з труднощами. Це були і перфоманс-проблеми, і перфоманс-команди і багато іншого. В результаті на додавання нового функціоналу йшли тижні, якщо не місяці — тобто кілька спринтів навіть на невелику фічу. Система виходила все більш заплутаною та перевантаженою, кількість багів у ній зростала. Незадоволені були і девелопери, і менеджери.

Менеджерам не подобався повільний розвиток продукту, а розробники були невдоволені складнощами при роботі над застосунком.

Новий порядок — перехід до мікросервісів

Команда вирішила докорінно змінити ситуацію та перейти до мікросервісів. Для цього ми поділили великий моноліт на кілька частин.

Онлайн-курс Pyton від Powercode academy.
Опануйте PYTHON з нуля та майте проект у своєму портфоліо вже через 4 місяця.
Приєднатися

Фактично він і так був поділений, але одну частину ми винесли як окремий проєкт. Визнаю, у цей момент про авторизацію ніхто не думав. Ми, скоріше, розглядали новий проєкт як Proof of Concept. Нам хотілося спробувати зробити частину колишнього моноліту на нових технологіях, з фронтендом, на сучасному фреймворку. Ми собі думали так: якщо ідея спрацює і всі будуть задоволені, поширимо цей підхід і на інші частини системи.

Для PoC-проєкту ми вигадали наступну схему. Є фронтенд та гейтвей, під ним розташовується оркестратор, которий пов’язаний із мікросервісами. Програма отримує запит від фронтенду і на його основі робить запити до відповідних мікросервісів:

Наша команда зробила фронтед на Angular, сервіси та оркестратор — на .NET 6, а також додала API Gateway, котрий працював як AuthGuard.

Якщо користувач не авторизований, гейтвей не пропускає запит і повертає помилку 401. При цьому легасі залишався таким, як був. Ми не мали не то що можливості, а навіть мети оверінженерити. Нам був потрібний лише PoC. Тому для авторизації ми використовували токен із легасі. Для цього додали в проєкт нову функціональність. Вона генерувала токен, а фронтенд забирав його та відправляв реквести з ним через API Gateway. Той, своєю чергою, парсив токен для оцінки валідності і пропускав запит далі.

Експеримент показав гарні результати. Ми поступово переклали частину системи на мікросервіси, і це позитивно оцінили всі сторони проєкту.

Однак зі збільшенням кількості цих елементів з’явилася проблема: не можна використовувати той ендпойнт на легасі, який видавав токени.

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

Ми замислились про підготовку якогось long-term-рішення. Це дозволило б закрити питання авторизації та аутентифікації в системі, застосувати однаковий підхід на всіх мікросервісах та на всіх поточних і майбутніх клієнтах. Але «переїзд» вимагав інфраструктурних змін. Тому стало зрозуміло: якісна аутентифікація та авторизація необхідні тут і зараз.

Курс UX/UI дизайнер сайтів і застосунків з Alice K.
Курс від практикуючої UI/UX дизайнерки, після якого ви знатимете все про UI/UX дизайн .
Реєстрація на курс

Завжди тримайте в голові базові терміни

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

  • Аутентифікація. Це процес у системі, який фактично має знайти відповідь на запитання «Хто ти?». У цей момент сервер «розпізнає» користувача.
  • Авторизація. Її часто плутають з аутентифікацією, вважаючи мало не синонімом, але ці терміни мають різні значення. У цьому випадку система визначає, до яких ресурсів має доступ впізнаний користувач.
  • OAuth 2.0 та OpenID Connect. Це протоколи та специфікації, які говорять про те, як правильно аутентифікувати та авторизувати користувача.
  • Grant Types. Частина протоколу OAuth 2.0. Показує, що потрібно зробити для отримання аксес-токена: які запити відправити, куди, коли, до якої бази, які мають бути респонси запитів тощо.
  • Клієнти. Програми, які дають запит до захищених даних. Найчастіше це фронтенд: мобільні застосунки, вебзастосунки, десктопні програми. Хоча клієнтом може бути й бекенд.
  • Ресурси. Програми у вигляді різноманітних API, які захищають дані за OAuth-протоколом.
  • JWT-токен. Те, що отримує клієнт для доступу до захищеного API. Грубо кажучи, це як квиток на поїзд: якщо він у вас є, вас пропускають у вагон і ви їдете далі.

Читайте також: Підробка неможлива: як влаштований токен і які завдання можна вирішити за допомогою JWT-авторизації

Курс Розмовної англійської від Englishdom.
Після цього курсу ви зможете спілкуватись з іноземцями і цікаво розкажете про себе.
Приєднатися

Grant Types

Окремо хочу поговорити про цю частину протоколу OAuth. Існує кілька часто використовуваних грант тайпів:

  • Password Grant Type;
  • Implicit Flow;
  • Authorization Flow;
  • Client Credentials.

Вказані моделі закривають усі основні пойнти. Таким чином ви можете розібратися, які з них та в яких ситуаціях варто використовувати.

Password Grant Type

Це один із перших грант тайпів, проте його вже не рекомендують використовувати. Незабаром цю модель можуть взагалі прибрати з OAuth-протоколу. Але розповісти про неї слід вже для того, аби ви розуміли, як робити не треба.

Як відбувається процес отримання токена в Password Grant Type? Уявімо користувача, який переходить на клієнта (припустимо, фронтенд-застосунок). На клієнті є спеціальна сторінка, де користувач вводить свої логін та пароль. Після цього клієнт надсилає на айдентіті-сервер таку інформацію: ClientSecret, ClientName, UserName тощо. Сервер після порівняння даних повертає аксес-токен. Це легкий шлях отримання доступу, адже тут потрібні лише налаштовані на сервері ClientId та ClientSecret, отримані від користувача UserName та UserPassword, а також Scopes — ідентифікатори визначення доступних користувачеві даних.

Як бачите, модель доволі швидка для реалізації. Завдяки цьому свого часу Password Grant Type стандартизував процес отримання аксес-токена. До цього можна було спостерігати зоопарк різних механізмів того, як, де і чому використовувати UserName та UserPassword. Інколи доходило до того, що логін та пароль відправлялися мало не в незашифрованому вигляді в хедерах до реквесту.

Сьогодні ж Password Grant Type вже застарів. Його ключовий недолік — прогалини в безпеці. Ви можете помітити, що в цій схемі клієнт може десь зберігати логіни та паролі. При цьому ендпойнт через стандартизованість грант тайпу добре відомий. Тому зловмисники можуть спробувати встановити в систему NPM-пакет для збору всіх даних і перехопити токен, що видається.

Курс English For IT: Communication від Enlgish4IT.
Почни легко працювати та спілкуватися з мультикультурними командами та міжнародними клієнтами. Отримайте знижку 10% за промокодом ITCENG.
Інформація про курс

Так злочинець дізнається, де хоститься сервіс авторизації, а також знатиме UserName та UserPassword. У результаті система буде скомпрометована. Більш того: хакер постійно отримуватиме валідний токен, бо матиме логін та пароль.

Також є проблема зберігання ClientId та ClientSecret на стороні клієнта. Це не найбезпечніше рішення для фронтенду, оскільки тут не здійснюється жодних редиректів. Прив’язка йде лише на основі цих параметрів. Якщо їх вкрасти, новий клієнт зможе підключити користувача до стороннього сайту та ввести ClientId і ClientSecret. І тоді користувачі будуть логінитися там й отримувати аксес-токен, який також буде викрадений. Усе це перекреслює попередні переваги Password Grant Type.

Implicit Flow

Цей підхід значно безпечніший, тому якийсь час він був дуже популярним. Implicit Flow досить зручний, його можна встановити без особливих зусиль. Однак він має кілька проблем із захищеністю, які сьогодні роблять цей механізм у протоколі OAuth 2.0 небажаним.

Для розуміння проблеми розберемо флоу покроково: 

  • Користувач звертається до клієнта, фронтенд-застосунку, та натискає на кнопку «Логін».
  • Після цього клієнт здійснює редирект користувача на сторінку /authorize на айдентіті-сервер. Цей ендпойнт демонструє користувачеві свою сторінку для введення логіна та пароля. Якщо вони правильні, аус-сервер видає клієнту аксес-токен:

  • На перший погляд в Implicit Flow все добре. Тут є лише одна логін-сторінка: ми самі її написали, і наш сервер авторизації відповідальний за неї. Для порівняння, за наявності п’яти клієнтів у Password Grant Type потрібно п’ять окремих сторінок для введення логіна та пароля. А тут достатньо налаштувати редирект на єдину логін-сторінку для будь-якої кількості клієнтів.

Однак у цієї схеми є значний недолік. В Implicit Flow все побудовано на редиректах, тобто на GET-запитах. Через це на фінальному кроці клієнт отримує аксес-токен із Query-параметрів. А тому шкідливе програмне забезпечення на клієнті може дістати токен із коду так саме, як ми це робимо за допомогою бібліотек для отримання доступу до системи. А це вже дуже серйозна проблема.

Хоча набагато гірше інше — ризик викрадення рефреш-токену. Аксес-токен надає доступ на 10 хвилин. Це чимало, але обмежує зловмисника, змушуючи його повторювати свої дії. З рефреш-токеном хакер вільний. Цей токен позбавляє користувача необхідності повторного редиректу на айдентіті-сервер після закінчення сесії.

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

Authorization Flow

Це рекомендований OAuth-підхід. Його сценарій схожий на Implicit Flow: користувач робить логін-клік на клієнта та перенаправляється на /authorize на айдентіті-сервер. Останній після підтвердження валідності логіна та пароля повертає не аксес- і рефреш-токени, а авторизаційний код в URL. Далі клієнт відправляє на айдентіті-сервер POST-запит із ClientId, ClientSecret та Authorization Code та отримує у відповідь POST-реквест із аксес- і рефреш-токенами.

Курс "Web design" від Web-academy.
Швидкий початок кар'єри у сфері IT! Опануйте професію веб-дизайнера — почніть самостійно керувати своїм часом й отримувати високий дохід вже за 9 тижнів.
Дізнатися більше

Через те, що такий реквест більш захищений, ніж Query-параметри, вся схема виходить більш надійною:

Маємо ідеальний флоу для отримання аксес- та рефреш-токенів, але завжди є «але».

Головне питання вже до команди, яка інтегрується з цим флоу: де зберігати аксес- та рефреш-токени?

Якщо в пам’яті, то при повторному вході треба заново авторизуватися. А якщо покласти токени в cookies, session чи local storage, то шкідливе malware-ПО легко вкраде їх. У результаті отримання доступу влаштовано набагато безпечніше, ніж раніше, але зберігання доступів вразливе. Я думаю, багато хто стикався з цією проблемою. Кожний вирішує її по-своєму, тому далі я розповім, як ми вирішили це питання.

Client Credentials

Раніше я описував схеми отримання доступу до API від клієнта (web, desktop, mobile). Client Credentials необхідний для доступу однієї програми API до іншої програми API.

Флоу організовано дуже просто: застосунок, який запитує доступ, відправляє ClientId та ClientSecret на айдентіті-сервер, а той після успішної перевірки цих параметрів повертає аксес-токен:

Така простота можлива тому, що при взаємодії API не потрібна інтерактивність — її фактично нікуди додати. Апішки працюють без фронтендів.

Курс English For Tech course від Enlgish4IT.
Лише 7 тижнів по 20-30 хвилин щоденного навчання допоможуть вам подолати комунікативні бар'єри. Отримайте знижку 10% за промокодом ITCENG.
Дійзнайтеся більше

До того ж, сама по собі API — це захищена частина системи, куди не так просто потрапити. Тому ми можемо спокійно записати параметри до сеттингів або в пам’ять та за необхідності дістати їх звідти для аус-сервера, щоб він видав токен.

Вибір авторизаційного сервера

Після оцінки основних авторизаційних флоу можна переходити до вибору сервера. Спочатку наша команда зупинилася на трьох кандидатах:

Кожен із них цікавий по-своєму.

Наприклад, Identity Server 4 — це опенсорсний продукт, де можна легко налаштовувати різні моменти. Особисто нашій команді він був добре знайомий з попередніх проєктів.

Опенсорним сервером є й Keycloak — один із найпопулярніших на сьогодні. Хоча ми були готові до вибору платної програми, яка дає всі механізми OAuth-протоколу «з коробки». У цьому випадку нас зацікавив Cognito, оскільки ми мали інфраструктуру на AWS.

При виборі сервера для нас важливими були наступні параметри:

  • можливості різноманітних флоу;
  • Курс QA Manual (Тестування ПЗ мануальне) від Powercode academy.
    Навчіться знаходити помилки та контролювати якість сайтів та додатків.
    Записатися на курс
  • підтримка SSO, Single Sign-On;
  • гарне співвідношення ціни та якості продукту;
  • гнучкі зміни та адаптації флоу під свої потреби;
  • доступність використання кастомних рішень;
  • простий та зручний механізм для міграції юзерів.

Keycloak

До його переваг відносяться просте та гнучке налаштування, підтримка SSO і кастомних флоу та адмінпанель прямо «з коробки», що заощаджує багато часу.

Але як мінімум для нашої команди, Keycloak має мінуси:

  1. Досить високий поріг входження. Якщо у вас є схожий досвід роботи із серверами, то буде простіше. Однак у нас такого фахівця не було.
  2. З Keycloak треба потурбуватися про хостинг (хоча, так, цю незручність ще можна вирішити).
  3. Source-code у Keycloak на Java. В нашому випадку на проєкті не було Java-розробника.
  4. Курс Fullstack Web Development від Mate academy.
    Стань універсальним розробником, який може створювати веб-рішення з нуля.
    Дізнатись про курс

При оцінці цих показників ми пам’ятали, що розробляємо long-term support продукт. Якби надалі виникли проблеми або нам просто була б незрозуміла поведінка системи, то довелося б щось змінювати в коді. Це призвело б до серйозних проблем та затримок, тому ми відмовилися від Keycloak.

Cognito

Цей продукт має багато переваг. Наприклад, це дешеве рішення для нашої кількості користувачів: активних і запланованих на майбутнє. Також у Cognito дуже швидкий сетап. Ви просто створюєте User Pool та клієнтів — і фронтенд-застосунок фактично готовий до авторизації. Сервер відразу можна використовувати.

Але і Cognito має низку недоліків, чи, скоріше, підозр на недоліки.

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

Виникли деякі складнощі при міграції користувачів. Припустимо, юзер вперше входить у систему. Вона перенаправляє його на сторінку Cognito для введення логіна та пароля і перевіряє, чи існує такий користувач у юзер-пулі. Його, звісно, немає, адже він уперше тут. Тоді на наступному етапі має бути лямбда-функція, яка звертається до легасі-застосунку з введеними логіном та паролем та перевіряє їх валідність. Якщо дані помилкові, видається Error. Якщо все правильно, то AWS, запам’ятавши ще на першому етапі логін та пароль, створює користувача в юзер-пулі після підтвердження лямбда-функцією всіх даних — і повертає аксес-токен:

Тепер уявімо, що користувач повторно заходить у систему і вводить вже знайомі для AWS логін та пароль. AWS перевіряє існування користувача в юзер-пулі (наприклад, за мейлом), у разі позитивного результату звіряє логін і пароль та за умови їх правильності видає аксес-токен. Нехитра та ефективна схема.

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

Через усі ці недоліки ми вирішили не брати Cognito.

Онлайн курс з промт інжинірингу та ефективної роботи з ШІ від Powercode academy.
Курс-інтенсив для отримання навичок роботи з ChatGPT та іншими інструментами ШІ для професійних та особистих задач, котрі допоможуть як новачку, так і професіоналу.
Записатися на курс

Identity Server 4

Наша команда вже мала досвід роботи з Identity Server 4, то спочатку це могло здатися «тим самим» рішенням. Цей авторизаційний сервер показав себе гнучким, із простою інтеграцією в .NET-застосунок. Також до його безумовних плюсів варто віднести наявність кастомних флоу, підтримку SSO та хорошу документацію.

Тут теж треба подбати про хостинг, але більш серйозним недоліком є припинення підтримки цього продукту після 22 листопада 2022 року. Враховуючи довгострокову перспективу розвитку нашого проєкту, подібне обмеження було неприйнятним. Ми рушили далі.

Duende

Порятунком для нас став цей авторизаційний сервер. Duende — це фактично продовження Identity Server 4 з усіма його перевагами, але без проблеми «22 листопада». Також маємо BFF Security Framework. Детальну інформацію про нього можете почитати на офіційному сайті.

Я ж виокремлю найцікавіші моменти:

  • Немає токенів у браузері. Аксес- та рефреш-токени повністю захищені від перегляду JS-кодом. Для цього до фронтенду додають бекенд, який створює сесію та cookies. При цьому бекенд із фронтендом розташовані на одному домені, оскільки перший є частиною другого. В результаті бекенд може сетапити куки під прапором HttpOnly. Далі вони при кожному запиті фронтенду додаються до реквесту і йдуть на бекенд, який проведе аутентифікацію та авторизацію користувача. Це помітно безпечніше за генерацію cookies з фронтенду, де їх може перехопити «чужий» JS-код.
  • Надання API для авторизаційних ендпойнтів. У звичній схемі авторизації фронтенд надсилає запит із набором з ClientId, ClientSecret, з реквестами та додаванням OAuth, повертає відповіді з кодом типу 401 тощо. Все це нам потрібно ретельно вибудовувати на клієнті. За допомогою BFF Security Framework можна створити для фронтенду кілька ендпойнтів, припустимо, BFF/Login. Як тільки фронтенд перейде на нього, фреймворк сам зробить всі налаштування для перередиректу на айдентіті-сервер, авторизації та повернення назад, обробки реквесту та збереження аксес- та рефреш-токенів у формі кук із прапором HttpOnly. Завдяки цьому на боці фронтенду можна взагалі не перейматися авторизацією. Весь процес автоматичний: достатньо надіслати BFF/Login або BFF/Logout із підставленою сесією для входу або виходу з системи.
  • Управління сесіями. У BFF Security Framework є окремий модуль для тонкого налаштування бекенд-сесії. Це може стати в нагоді в багатьох ситуаціях. Наприклад, менеджмент сесій знадобиться за дуже великих cookies. Також він допоможе для сетапу Backchannel logout, коли користувач розлогінюється з однієї частини системи чи з усіх.
  • Менеджмент токенів. Цей фреймворк надає безліч API для керування та зміни токенів. Наприклад, можна отримати аксес-токен і за необхідності переробити чи доопрацювати його.
  • Швидка та легка інтеграція. BFF Security Framework це насправді не GET-пакет. Практично всі надані функціональності легко перевизначаються. Наприклад, можна кастомізувати ендпойнти для логіна та логаута. Також можна зберігати сесію як у MSSQL, так і зробити своє сховище. Те саме стосується й згаданого токен-менеджменту.

Усе це дозволило нам прийняти правильне рішення. Duende виявився оптимальним авторизаційним сервером для наших завдань.

Курс Frontend від Mate academy.
Frontend розробник може легко створити сторінки вебсайту чи вебдодаток. Тому після курсу ви станете затребуваним фахівцем у сфері, що розвивається.
Інформація про курс

Результат

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

При цьому ми прибрали API Getway, і фронтенд спілкується безпосередньо з оркестратором. До оркестратору ми додали BFF Security Framework. Усе хоститься на одному домені — це вимога BFF Security Framework. За рахунок цього токен надається в оркестратор, що дозволило побудувати ще й antiforgery-захист фронтенду. Далі оркестратор аутентифікує та авторизує користувача та відправляє реквести на мікросервіс. Усе просто і, що найважливіше, надійно:

Якщо узагальнити результати всієї нашої дослідницької та девелоперської роботи, то ми виграли відразу в кількох моментах:

  • Замість того, щоб вигадувати велосипед з PoC та «кастомним» авторизаційним сервером, який видає токени, ми дійшли стандартизованого підходу для цієї сфери. Тому тепер наш продукт легко інтегрувати з іншими рішеннями.
  • Використовуємо однаковий підхід для різних клієнтських застосунків. Після налаштування флоу для першого клієнта додавати 2-й, 3-й, 5-й, 10-й та всі інші вже не буде проблемою. За рахунок цього скорочується час на інтеграцію нових клієнтів.
  • Маємо актуальний, рекомендований OAuth-протоколом принцип Authorization Grant Type.
  • У нашому випадку BFF Security Framework показав себе гарним рішенням, яке на сьогодні покриває всі наші потреби та економить час. З ним ми більше не зберігаємо аксес- та рефреш-токени у браузері. Використовуємо фактично весь його функціонал: у нас є й Backchannel logout, й бекенд-сесія. Авторизацію та аутентифікацію прибрали з фронтенду.

Звісно, у вашому випадку все може скластися інакше, і ви оберете, скажімо, Keycloak. Більш того, якщо вам не потрібні додаткові механізми редиректів та й загалом немає складної логіки, тоді й Cognito або Azure Active Directory чудово підійдуть. Тому при виборі айдентіті-сервера передусім раджу грамотно оцінювати свої завдання та можливості кожного існуючого авторизаційного сервера.

Бізнес англійська від Englishdom.
Тут навчають за методикою Кембриджу, завдяки якій англійську вивчили понад 1 мільярд людей. Саме вона використовується в найкращих навчальних закладах світу, і саме за нею створені курси.
Інформація про курс

Якщо ви знайшли помилку, будь ласка, виділіть фрагмент тексту та натисніть Ctrl+Enter.

Курс English For IT: Communication від Enlgish4IT.
Почни легко працювати та спілкуватися з мультикультурними командами та міжнародними клієнтами. Отримайте знижку 10% за промокодом ITCENG.
Інформація про курс

Цей матеріал – не редакційний, це – особиста думка його автора. Редакція може не поділяти цю думку.

Топ-5 найпопулярніших блогерів березня

PHP Developer в ScrumLaunch
Всего просмотровВсього переглядів
2229
#1
Всего просмотровВсього переглядів
2229
Founder at Shallwe, Python Software Engineer (Django/React)
Всего просмотровВсього переглядів
111
#2
Всего просмотровВсього переглядів
111
Career Consultant в GoIT
Всего просмотровВсього переглядів
93
#3
Всего просмотровВсього переглядів
93
CEO & Founder в Trustee
Всего просмотровВсього переглядів
92
#4
Всего просмотровВсього переглядів
92
Рейтинг блогерів

Найбільш обговорювані статті

Топ текстів

Ваша жалоба отправлена модератору

Сообщить об опечатке

Текст, который будет отправлен нашим редакторам: