Русский
Русский
English
Статистика
Реклама

Авторизация пользователя

Авторизация и аутентификация на NodeJs и Socket.io и проблемы вокруг

19.01.2021 12:23:47 | Автор: admin

На текущий момент я работаю в компании МегаФон тимлидом фронта. С начала 2020 года мы в команде МегаФона разрабатываем собственную платформу Интернета вещей. Так как в таком процессе нагрузка на бэк-энд разработчиков стала колоссальной, а фронт не так активно задействован, внутри отдела было принято решение отдать всю веб-часть в руки моей команды. Очевидно, что мы взяли NodeJs с ExpressJS, и занялись построением серверной архитектуры.

Для корректного доступа к собранным с устройств данным нужна была авторизация, чтобы понимать, кто и что может получить/сделать. Про очевидный путь с passportJs, думаю, нет смысла рассказывать. По логину и паролю мы стали отдавать jwt токен, и изначально на этом успокоились.

После этого нам потребовалось хранить в сессии данные, специфичные для каждого пользователя. Логичным решением было бы использовать сам jwt токен, хранить информацию в нем и гонять от клиента к серверу. Однако, данное решение не подходило нам из-за использования веб-сокетов (в нашем случае мы взяли socket.io), так как в данном протоколе передача хедера Authorization с jwt токеном невозможна (в соответствии со стандартом). Единственный вариант - передавать хедер в параметрах url. Но это не очень здорово - токены будут легко видны во всех логах всех прокси-серверов. Хорошим решением оказалось использование сессии, которая хранится полностью на серверной стороне, и по сети ходит лишь id этой сессии. Мы выбрали - express-session.

Объединенная сессия

Отдельной проблемой стала необходимость получения актуального состояния сессии и возможность его изменения в событиях веб-сокетов. Для этого идеально подошел пакет - express-socket.io-session. Правда, пришлось поколдовать над её подключением:

Изменили подключение сессии и настройки кук:

this.store = new pgSession({          pool: pgPool,          tableName: SESSION_TABLE      });this.session = expressSession({    name: SESSION_KEY,    secret: SESSION.secret,    resave: false, // важно, для того, чтобы сессия не перезаписывалась на каждый чих    rolling: true,    saveUninitialized: true, // нужно для выдачи куки даже неавторизированному пользователю    proxy: true,    cookie: {        secure: true, // обязывает производить передачу по ssl        maxAge: SESSION_DURATION,        sameSite: 'none' // чтобы можно было отдавать на разные поддомены    }    store: this.store});

Мы написали обработчики сессии таким образом, чтобы сессия подгружалась до начала обработки события, и сохранялась, если необходимо:

const asyncHandlerExtended = (fn, socket) => (data) => {    const cb = async () => {        await reloadSession(socket.handshake.session);        await fn({ socket, data });        await saveSession(socket.handshake.session);    };    return Promise.resolve(cb()).catch((err) => {        socket.emit('error', err);    });};

Собрали все вместе при настройке сокетов:

import sharedSession from 'express-socket.io-session';import io from 'socket.io';const resultSocket = nameSpace ? this.io.of(nameSpace) : this.io;resultSocket.use(sharedSession(session, { autoSave: true }));

Разделение сокетов по ролям

Дальше нам нужно понимание того, кому и какие события можно получать на сокетах, а какие - нет. Для этого отлично подходит механизм комнат в socket.io. Он позволяет серверу формировать пространства, в которые можно "запускать" пользователей и эмитить в них разные события. Мы выделили под каждую из ролей пользователей отдельную комнату (например комната adminRoom - пространство для событий, которые могут идти/поступать только для администраторов), а общее пространство теперь у нас считается "публичным" и доступно для всех подключенных, но не авторизованных пользователей. Таким образом, процесс получения доступов на сокетах выглядит так:

  1. Клиент аутентифицируется по http, по паре логин/пароль, получает в ответ jwt токен и куку с id сессии.

  2. Далее юзер коннектится к нашей точке входа для socket.io (например: localhost:8080/sockets). Теперь у него есть доступ до публичных событий на наших сокетах.

  3. Если он хочет получить доступ до всех наших событий, которые ему доступны по роли, то он отправляет событие auth_login по сокетам, с jwt токеном, который он получил от http авторизации.

  4. Система проверяет токен и по результатам проверки генерирует одно из двух событий.

    1. auth_loginFailed - пользователю не будут предоставлены доступы, так как токен кривой или просрочен

    2. auth_loginSuccess - все хорошо, можно продолжать

  5. Если проверка прошла успешно, то сервер добавляет пользователя во все пространства предоставленные ему по его роли.

  6. Пользователю теперь доступны аутентифицированные и скрытые ранее события.

  7. Когда у токена проходит "срок годности", сокеты генерируют событие auth_expire, говорящее, что более пользователю недоступны ранее предоставленные комнаты.

Token steal

Вишенкой на торте в данной картине механизма авторизации/аутентификации стал результат изучения проблемы кражи токенов. Ради минимизации рисков от попадания в такую ситуацию, было решено улучшить механизмы работы с авторизационным токеном. Во время исследования данной темы наткнулся на статью на хабре - Зачем нужен Refresh Token, если есть Access Token?. Очень советую ознакомиться, но если кратко, то вот результирующая цитата:

Таким образом, схема refresh + access токен ограничивает время, на которое атакующий может получить доступ к сервису. По сравнению с одним токеном, которым злоумышленник может пользоваться неделями и никто об этом не узнает.

Однако, у нас уже есть два токена:

  • id серверной сессии от express-session, который ходит в куках, всегда

  • jwt токен, который генерируется после логина пользователя

Поэтому было бы логично реализовать похожий механизм, как и в OAuth2. Для этого мы стали хранить jwt токен в сессии и сравнивать его с полученным от клиента при проверке аутентификации. Остается только одна проблема - несколько токенов в одной сессии. Необходимо это для того, чтобы иметь возможность войти и в админку, и на фронт. Для этого считаем, что клиент передаст свое "название" при логине, и под этим названием мы и сохраним токен в сессию, а также записываем само название внутрь токена для дальнейшей проверки. За счет вышеописанного получается следующая схема взаимодействия:

  1. Клиент отправляет пару логин и пароль, плюс к этому уникальный ключ - название себя (то есть имя приложения).

  2. Система, если пара логин и пароль найдена, генерирует jwt токен, включая в него название клиента (ключ), отправляет его клиенту и записывает в сессию.

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

  4. Если вдруг токен и куку украл злоумышленник, то при первом же рефреше с любой из сторон (клиент или злоумышленник), сторона, которая попытается обратиться со старой парой токен + кука, получит негативный результат. Система поймет, что токен не соответствует тому, что хранит сессия и очистит сессию полностью, что привет к выбросу и клиента и злоумышленника, а факт кражи будет зафиксирован в системе.

  5. Если же все хорошо, то само собой мы отдадим данные =)

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

В заключение

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

Подробнее..

В играх детям нравятся атрибуты взрослой жизни как мы разрабатывали обучающую игру для младших школьников

14.07.2020 18:11:56 | Автор: admin
Лето время каникул и низкий сезон для образовательных проектов. За эти три месяца дети могут забыть до четверти знаний, полученных в классе и дома. Для того чтобы вовлечь детей в повторение математики на каникулах, мы делаем образовательные игры.

Сегодня я расскажу о нашем опыте в такой лернификации мультиплеерных игровых механик и адаптации их для детей от семи до одиннадцати лет на примере новой онлайн-игры Формула 1+1. За несколько месяцев работы в тестовом режиме (игра доступна только 10% пользователей платформы) в ней приняли участие 95 тыс. игроков, а самые упорные провели в ней больше 100 часов, то есть более 6 тыс. игр. Я постараюсь показать, как мы реализуем лернификацию процесс, когда мы не пытаемся сделать интересной учебу, а напротив добавляем элемент обучения в игру.


Карта гонок в игре Формула 1+1

Игра Формула 1+1 мультиплеерные гонки, где на скорость автомобиля влияет решение примеров устного счета: чем быстрее и больше правильных ответов будет давать игрок, тем быстрее он будет ехать. За победу в заезде игрок получает опыт (поднимается в рейтинге) и монеты, на которые можно покупать новые, более мощные на вид машины.

Какие цели мы ставили


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

Мы стремились:

Учесть особенности детского восприятия


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

Показать ценность игры взрослым


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

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

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

Игровая механика


Ядро геймплея


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

В пользу мультиплеера выступает наш удачный опыт проведения различных соревнований на портале и обилие успешных мультиплеерных мобильных игр на рынке: Brawl Stars, Clash of Clans, Clash Royale и подобные. Так у нас получились гонки, где в онлайн-заезде два ученика соревнуются в том, кто быстрее доедет до финиша.

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

Гоночные заезды недолгие всего пять арифметических примеров с тремя вариантами ответов, которые одновременно показываются обоим игрокам. Вся гонка занимает примерно полторы минуты.


Выбирая правильный ответ, игрок продвигает свой автомобиль к финишной черте. Циферблат слева показывает номер текущего вопроса, справа таймер, отсчитывающий выделенное на ответ время.

Второстепенные механики


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

Чтобы добавить гонке вариабельности (читай реиграбельности), мы придумали бонус за быстрые ответы. Если игрок два раза подряд дает правильные ответы быстрее соперника, его автомобиль еще чуть-чуть продвигается вперед.

Заезды можно проводить на разных трассах. Всего их пять. На каждой свой тип арифметических примеров: сложение, вычитание, умножение, деление и микс, где встречаются все примеры.

В каждой категории вопросы генерируются во время игры. В отличие от заранее прописанных примеров, этот способ не только сильно расширяет количество заданий, которые может встретить ученик, но и дает нам возможность регулировать их сложность в зависимости от класса. Мы выделили четыре уровня сложности, которые соответствуют нормам 1, 2, 3 и 4 (и выше) класса.

Например, ученики 1 класса умеют считать только в пределах 20, а в 4-м дети решают примеры с трехзначными слагаемыми и переходом через один десяток. С заданиями последнего уровня без калькулятора справится не каждый взрослый на один пример отведено всего десять секунд. Попробуйте прямо сейчас быстро выполнить задание на сложение с двумя переходами через десятки: 574 + 349.



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

Хотя у портала и огромная аудитория в 8 млн учеников, игрокам может не хватать соперников в онлайне. Чтобы не заставлять детей долго ждать подходящего партнера по игре, мы ввели в игру ботов. Бот-соперник выглядит как обычный игрок. На основе рейтинга игрока боту присваивается соотношение правильных и ошибочных ответов: сильные игроки получают сильного бота, слабые слабого.

Чтобы избежать договорных матчей, мы не даем вознаграждений за заезды с друзьями.

Персонаж: детали и нюансы


Три составляющие персонажа игрока


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

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

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



Авторизация 0+


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

Мотивация


1. Награда за сыгранную партию


В конце заезда ученик всегда получает награды очки опыта и монеты. Очки опыта нужны, чтобы повысить свой уровень. Это самый простой инструмент мотивации. Чем больше ты играешь, тем выше у тебя уровень, и не важно, хорошо ты играешь или нет. За монеты можно купить новые автомобили, более крутые и красивые.

Кстати, когда мы тестировали разные модели авто с детьми, самыми крутыми они называли не суперкары и не новинки автопрома, а модели, которые видели в семье.



2. Прокачка в нескольких направлениях


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

3. Личный рейтинг


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

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

Рейтинг Эло метод расчета относительной силы игроков в парных играх. Все новые игроки получают стартовое значение рейтинга. После гонки с соперником рейтинг обоих изменяется в зависимости от победителя и значений рейтингов обоих игроков. Если ребенок выиграл у партнера с более высоким местом в рейтинге, его рейтинг вырастет больше, чем если бы он выиграл у соперника на более низком месте. Таким образом сильный игрок, постоянно выигрывая, будет увеличивать свой рейтинг, пока не дойдет до равных ему, а шанс на победу не составит 50/50.


Игроки с меньшим уровнем могут оказаться выше в рейтинге.

4. Рейтинг класса


Как показывает наш опыт, дети с удовольствием соревнуются целыми классами, поэтому мы добавили в игру и рейтинг класса. Чтобы решить проблему разной численности классов (от нескольких учеников до 35) и чрезмерного рвения к первым местам, мы разработали отдельную систему расчета классного рейтинга.
  1. Очки для рейтинга класса игрок получает только за первые пять заездов в день в режиме Микс.
  2. Количество получаемых очков зависит от численности класса. Если каждый ребенок в каждом классе будет участвовать в пяти гонках в день и выигрывать, то эти классы наберут одинаковую (максимальную) сумму очков класса вне зависимости от количества учеников. Эта сумма становится основой для расчета баллов рейтинга для всех классов в игре.
  3. Эта максимальная сумма должна хорошо (без остатка или с наименьшим остатком) делиться на количество дней в месяце, на пять гонок, на различное количество детей в классе и на три (за проигрыш полагается от выигрыша). Это важно, потому что ученики 14 классов еще не знают дробей.
  4. Для подбора наилучшего значения этой максимальной суммы мы написали скрипт, который перебрал несколько сот тысяч значений и выбрал наиболее подходящее число 890 100. Сумма вознаграждения за заезд получается в виде целого числа, при этом не громоздкой. Например, в классе из 30 человек за победу в заезде игрок получит 198 очков класса.


Матрица расчета баллов

Вывод


У нас получилась игра, очень похожая на обычную онлайн-игру, но с реальным образовательным эффектом, при этом адаптированная для детей 711 лет. Главная механика игры служит улучшению навыка счета у ребенка. Первые результаты показывают, что дети готовы проводить в ней огромное количество времени: на момент написания этой статьи мы насчитали более 95 тыс. активных игроков (при том, что игра все еще находится в тестовом режиме и доступна только 10% учеников Учи.ру). Реакция родителей на Формулу 1+1 тоже положительная.

Самое трудное и интересное в разработке игры впереди. Нам предстоит подробнее разобраться, что именно сделало игру популярной, а что было лишним, изучить данные множества метрик и провести интервью с детьми, родителями и учителями.
Подробнее..

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru