Содержание
- 1 Стратегии аутентификации
- 1.1 Настройка приложения
- 1.2 Добавление зависимостей проекта
- 1.3 Создание модели Mongoose
- 1.4 Конфигурация Mongo
- 1.5 Конфигурация Passport
- 1.6 Сериализация и десериализация экземпляров объекта пользователя
- 1.7 Использование стратегий Passport
- 1.8 Стратегия авторизации
- 1.9 Стратегия регистрации
- 1.10 Создание маршрутов
- 1.11 Создание представлений Jade
- 1.12 Реализация функции выхода из системы
- 1.13 Защита маршрутов
- 1.14 Заключение
- 2 5.1. Аутентификация¶
- 3 5.2. Сессии в Express¶
- 4 5.3. Сессии в MongoDB¶
- 5 5.4. Контроль доступа¶
- 6 5.5. RESTful подход к сессиям¶
- 7 5.6. Модель пользователя¶
- 8 5.7. Сохранение пользователей и регистрация¶
- 9 5.8. Заключение¶
Реализация надежных систем аутентификации для любого приложения может стать непростой задачей, приложение Node.js здесь не исключение.
В этой статье мы создадим с нуля Node.js приложение и используем относительно новое, но уже очень популярное связующее программное обеспечение — Passport , чтобы разобраться с проблемами аутентификации.
Позиционируясь как связующее программное обеспечение, Passport отлично справляется с тем чтобы выделить среди других элементов веб-приложений именно аспекты аутентификации.
Это позволяет Passport легко настроить любое веб-приложение на базе Express , так же, как мы можем просто настроить другой связующий Express -софт, например, logging , body-parsing , cookie-parsing , session-handling и т.д.
В этой статье мы приведем обзор базовых возможностей фреймворков Node.js и Express , при этом делая упор на вопросы аутентификации. Хотя само приложение Express мы создадим с нуля и расширим его, добавив маршруты и аутентификацию некоторых из этих маршрутов.
Стратегии аутентификации
Passport предлагает нам на выбор свыше 140 механизмов аутентификации. Вы можете проводить аутентификацию с помощью локального/удаленного экземпляра объекта базы данных или использовать единый вход с использованием OAuth , предоставляемый Facebook , Twitter , Google и т.д., для аутентификации в ваших аккаунтах социальных медиа.
Или вы можете выбрать из обширного списка провайдеров , которые поддерживают аутентификацию с помощью Passport и предоставляют для него модуль узла.
Но не беспокоитесь: Вам не нужно включать все стратегии / механизмы , которые вашему приложению и не нужны. Все эти стратегии являются независимыми друг от друга и упакованы в виде отдельных модулей узлов, которые не включаются по умолчанию при установке связующего программного обеспечения Passport : npm install passport .
В этой статье мы будем использовать локальную стратегию аутентификации Passport и аутентификацию пользователей с помощью локально настроенного экземпляра объекта Монго DB , хранящего информацию о пользователе в базе данных.
Для использования стратегии локальной аутентификации, мы должны установить модуль passport-local : npm install passport-local .
Но подождите минутку: перед тем, как вы запустите терминал и начнете выполнение этих команд, давайте все же рассмотрим построение приложения Express с нуля и добавление к нему некоторых маршрутов ( для авторизации, регистрации и главной страницы ), а затем добавим связующий софт для аутентификации.
Обратите внимание, что в этой статье мы будем использовать Express 4 , но с некоторыми незначительными отличиями Passport одинаково хорошо работает с Express 3 .
Настройка приложения
Если вы еще этого не сделали, то установите Express и express-generator для создания шаблонных приложений. Для этого нужно просто выполнить в терминале команду express passport-mongo . Сформированная структура приложения должна выглядеть следующим образом:
Давайте удалим некоторый функционал по умолчанию, который нам вовсе необязательно использовать — удалите маршрут users.js и уберите все его ссылки из файла app.js .
Добавление зависимостей проекта
Откройте файл package.json и добавьте в него зависимости для passport и модуля passport-local :
Так как мы будем сохранять информацию о пользователе в MongoDB , мы будем использовать Mongoose в качестве инструмента моделирования данных объекта. Также установить и сохранить зависимость для package.json можно с помощью команды:
package.json должен выглядеть следующим образом:
Теперь установите все зависимости и запустите шаблонное приложение, выполнив команду npm install && npm start . Она загружает и устанавливает все зависимости и запускает сервер узла. Вы можете проверить установку приложения Express , перейдя по адресу: http://localhost:3000/ , но там вы пока еще ничего особенного не увидите.
Очень скоро мы это изменим, создав полноценное Express приложение, которое запрашивает вывод страницы регистрации для нового пользователя, обрабатывает вход в систему зарегистрированного пользователя и аутентифицирует зарегистрированного пользователя, используя Passport .
Создание модели Mongoose
Так как мы будем сохранять информацию о пользователях в Mongo , давайте создадим модель пользователя в Mongoose и сохраним ее в файле models/user.js нашего приложения:
Мы создаем модель Mongoose , с помощью которой мы можем выполнять CRUD операции в основной базе данных.
Конфигурация Mongo
Если у вас нет установленного локально Mongo , мы рекомендуем использовать облачные сервисы баз данных, такие как Modulus или MongoLab .
Создание рабочего экземпляра MongoDB с их помощью не только бесплатно, но кроме того это можно сделать всего лишь в несколько кликов.
После создания базы данных на одном из этих сервисов, вы получите URI базы данных, наподобие mongodb://:@novus.modulusmongo.net:27017/ , который можно использовать для выполнения CRUD -операций с базой данных.
Я рекомендую сохранить конфигурацию базы данных в отдельный файл, который можно будет подтянуть, когда это будет необходимо. Таким образом, мы создаем модуль узла db.js , который выглядит следующим образом:
Теперь мы используем эту конфигурацию в app.js и подключаемся к ней с помощью Mongoose API :
Конфигурация Passport
Passport только обеспечивает механизм для обработки аутентификации, не заботясь о реализации обработки своей же сессии, для этого мы будем использовать express-session .
Откройте файл app.js и вставьте перед настройкой маршрутов приведенный ниже код:
Это необходимо, поскольку мы хотим, чтобы сеансы пользователей были стабильными. Перед запуском приложения мы должны установить express-session и добавить его в список зависимостей файла package.json . Для этого наберите в командной строке — npm install —save express-session .
Сериализация и десериализация экземпляров объекта пользователя
Для Passport также необходима сериализация и десериализация экземпляра объекта пользователя из сессии сохранения в целях поддержки текущей сессии, так чтобы каждый последующий запрос не содержал учетные данные пользователя. Для этого предназначены два метода serializeUser и deserializeUser :
Использование стратегий Passport
Теперь мы определим стратегии Passport для обработки авторизации и регистрации. Каждая из них будет экземпляром стратегии локальной аутентификации Passport и будет создаваться при помощи функции passport.use() .
Мы используем connect-flash , что поможет нам в обработке ошибок, предоставляя флэш-сообщения, которые могут выводиться пользователю при возникновении ошибки.
Стратегия авторизации
Стратегия авторизации выглядит следующим образом:
Первый параметр passport.use() является именем стратегии, которое будет использоваться для идентификации этой стратегии при последующем применении. Вторым параметром является тип стратегии, которую вы хотите создать, здесь мы используем username-password или LocalStrategy .
Следует отметить, что по умолчанию LocalStrategy ищет учетные данные пользователя в параметрах username и password , но мы можем также использовать любые другие проименованные параметры.
Переменная конфигурации passReqToCallback позволяет нам получить доступ к объекту request в функции обратного вызова, благодаря чему, в свою очередь, мы имеем возможность использовать любой параметр, связанный с запросом.
Далее, мы используем Mongoose API , чтобы найти пользователя в нашей основной базе пользователей и проверить, является ли он доверенным пользователем или нет.
Последний параметр в нашем обратном вызове done указывает на используемый метод, с помощью которого мы сообщаем модулю Passport об успешном выполнении действия или ошибке.
Чтобы идентифицировать сбой либо первый параметр должен содержать ошибку, либо второй параметр должен содержать значение false . Для обозначения успешного прохождения действия первый параметр должен иметь значение null , а второй — truthy , в этом случае объект request становится доступен.
Поскольку пароли по своей природе являются уязвимым местом, мы всегда должны шифровать их перед сохранением в базу данных. Для этого мы используем bcrypt-nodejs , который помогает шифровать и расшифровывать пароли:
Если вам неудобно работать с отдельными фрагментами кода, и вы предпочитают видеть полный код в действии, вы можете просмотреть его здесь .
Стратегия регистрации
Теперь мы определяем следующую стратегию, которая будет обрабатывать регистрацию нового пользователя и создавать его учетную запись в основной базе данных Mongo DB :
Здесь мы снова использовали Mongoose API , чтобы выяснить, существует ли пользователь с данным именем пользователя или нет. Если нет, то создаем нового пользователя и сохраняем информацию о нем в Mongo .
В противном случае возвращаем ошибку с помощью обратного вызова done и флэш-сообщения. Обратите внимание, что мы используем bcrypt-nodejs для создания хэша пароля перед его сохранением:
Создание маршрутов
В общем, схема нашего приложения будет выглядеть так:
Теперь мы задаем маршруты для применения в следующем модуле, который принимает экземпляр паспорта созданного ранее в app.js . Сохраните этот модуль в файле routes/index.js :
Наиболее важной частью приведенного выше фрагмента кода является использование passport.authenticate() для делегирования аутентификации стратегиям login и signup , когда HTTP POST выполнен для маршрутов /login и /signup соответственно.
Обратите внимание, что не обязательно называть стратегии соответственно пути маршрута, им можно назначать произвольные имена.
Создание представлений Jade
Далее, мы создаем следующие два представления для нашего приложения:
- layout.jade — содержит базовую структуру и информацию о стилях;
- index.jade — содержит страницу авторизации, включающую форму входа и опции для создания новой учетной записи:
Благодаря Bootstrap наша страница авторизации теперь выглядит следующим образом:
Нам также потребуются еще два представления для ввода регистрационных данных и для домашней страницы приложения:
- register.jade — содержит форму регистрации;
- home.jade — выводит приветствие и данные авторизовавшегося пользователя.
Если вы не работали с Jade , здесь можете найти документацию по нему.
Реализация функции выхода из системы
Passport , будучи связующим софтом, позволяет добавлять определенные свойства и методы к объектам запросов и ответов, что в свою очередь дает возможность добавить очень удобный метод request.logout() , который отменяет сеанс пользователя:
Защита маршрутов
Passport также предоставляет возможность защитить доступ к маршруту, который не предназначен для анонимных пользователей.
Это означает, что если некоторые пользователи пытается получить доступ к маршруту http://localhost:3000/home без авторизации в системе, они будут перенаправлены на главную страницу следующим образом:
Заключение
Passport не является единственным возможным решением, когда речь заходит об аутентификации приложений Node.js , существуют и альтернативные варианты, такие как EveryAuth .
Но большое количество модулей, гибкость, поддержка сообщества и тот факт, что он является просто связующим программным обеспечением, действительно выделяет Passport из череды других приложений.
Я начал изучать Node.js и начал делать авторизацию пользователей, но попал в тупик. Вот что я хочу реализовать:
— пользователь входит в систему
— перенаправляет его на страницу пользователя
— данные об отпуске пользователя отображаются на странице пользователя
Но я застрял на последнем пункте. Как я могу передать req.session.user в запрос get или есть другой способ? Вот мой код:
index.js
- Вопрос задан 17 авг.
- 143 просмотра
Видимо, вам нужно возвращать какую-то html-страницу обратно пользователю, и на эту страницу предварительно отрисовывать то, что нужно.
Для этого можно использовать один из многочисленных шаблонизаторов.
Добро пожаловать в пятую часть руководства по созданию веб-приложения с помощью Node.js. В рамках серии уроков будет рассказано про основные особенности и трудности, которые возникают при работе с Node.js.
5.1. Аутентификация¶
Мы уже сделали достаточно полезное приложение. Но оно было бы еще более полезным, если бы имело какое-либо подобие системы аутентификации. Даже не смотря на то, что в миру набирают обороты такие технологии, как OpenID и OAuth, большинство коммерческих проектов предпочитают иметь свою собственную систему входа.
Обычно она реализуется с помощью сессий:
- Пользователь заполняет форму, указывая логин и пароль
- Пароль шифруется с помощью хэш-алгоритма
- Полученное значение сравнивается с тем, что хранится в БД
- Если они совпадают, то генерируется сессионный ключ, идентифицирующий пользователя
Для реализации пользовательских сессий нам нужно следующее:
- Пользователь в БД
- Сессии, в которых можно хранить идентификатор пользователя
- Шифрование пароля
- Возможность ограничения доступа к тем URL, для которых требуется залогиненный пользователь
5.2. Сессии в Express¶
В основе сессий в Express лежит соответствующий средний слой (middleware) из Connect, который, в свою очередь, опирается на механизм хранения данных. Существует хранилище в памяти, а так же сторонние хранилища, включая connect-redis и connect-mongodb. В качестве альтернативы так же можно рассматривать cookie-sessions, который хранит данные сессии в пользовательской куке (cookie).
Поддержка сессий может быть включена следующим образом:
Размещение этого кода в разделе конфигурации приложения очень важно. В случае ошибки сессионная переменная не появится в объекте запроса. Я разместил этот кусок между bodyDecoder и methodOverride . Полную версию кода вы можете посмотреть на GitHub.
Теперь в HTTP-обработчиках будет доступна переменная req.session :
5.3. Сессии в MongoDB¶
Для поддержки сессий в MongoDB необходимо установить connect-mongodb:
Работает connect-mongodb так же как и любое другое хранилище сессий. Во время настройки приложения необходимо указать детали соединения:
Большая часть этого кода не понадобилась бы, если бы авторы API реализовали стандартный формат настроек соединения. Я написал функцию, извлекающую настройки соединения из Mongoose. В этом примере, переменная db хранит экземпляр соединения Mongoose, который ждет настроек соединения в виде URI. Этот формат, кстати, мне более всего симпатичен из-за своей простоты и легкости для запоминания. Строку соединения я сохраняю с помощью app.set .
При работе с Express бывает полезно использовать app.set(‘name’, ‘value’) . Так же следует запомнить, что для доступа к настройке следует использовать app.set(‘name’) , а не app.get .
Теперь, запустив в консоли Mongo db.sessions.find() , можно увидеть все созданные сессии.
5.4. Контроль доступа¶
Express предоставляет элегатный способ по ограничению доступа для залогиненных пользователей. При определения HTTP-обработчика может быть задан необязательный параметр маршрутизации:
Теперь доступ к адресу (URL), требующему только авторизованных пользователей, может быть ограничен простым добавлением loadUser в соответствующий HTTP-обработчик. Вспомогательная функция принимает те же параметры, что и обычный обработчик, плюс один дополнительный параметр next . Последний позволяет использовать дополнительную логику перед непосредственным вызовом функции обработчика адреса. В нашем проекте, пользователь загружается, используя сессионую переменную user_id . Если пользователь не найден, то функция next не вызывается и происход переадресация на окно ввода логина/пароля.
5.5. RESTful подход к сессиям¶
Я сделал сессии таким же образом, как и документы. Добавил адреса для создания, удаления и получения сессий:
5.6. Модель пользователя¶
Модель пользователя User немного сложнее, чем модель документа Document , так как в ней будет содержаться код связанный с авторизацией. Я использовал следующую стратегию, которую, вероятно, вы уже видели ранее в объектно-ориентированных веб фреймворках:
- Пароли хранятся в виде хэша
- Аутентификация выполняется сравнением зашифрованного текста, указанного пользователем, и паролем-хэшем, хранящимся в БД для пользователя
- Виртуальное свойство password хранит пароль в текстовом виде для удобства в формах регистрации и входа
- У свойства есть сеттер, который автоматически конвертирует текст пароля в хэш перед сохранением
- Используется уникальный индекс для поля email, чтобы гарантировать, что у каждого пользователя свой собственный email
Шифрование пароля использует стандартную Node.js библиотеку crypto :
encryptPassword — метод экземпляра, возвращающий sha1-хэш для текстового пароля и некоторой соли. Соль генерируется перед шифрованием в сеттере пароля:
Солью может быть всё, что угодно. Я, в данном примере, генерирую случайную строку.
5.7. Сохранение пользователей и регистрация¶
Mongoose позволяет изменять поведение модели при сохранении с помощью переопределения метода save :
Я переопределил метод save , чтобы была возможность обработки неудачного сохранения модели. Это облегчит обработку ошибок при регистрации:
Пока не выводится никаких сообщений об ошибках. Это будет добавлено в одной из следующих частей.
Несмотря на всю простоту этой проверки, индекс критически важен для приложения:
Эта проверка предотвратит дублирование пользователей при сохранении.
5.8. Заключение¶
После коммита 03fe9b2 мы имеем следующее:
- Сессии в MongoDB
- Модель пользователя с поддержкой шифрования пароля алгоритмом sha-1
- Контроль доступа к документам
- Регистрацию и аутентифкацию пользователей
- Управление сессиями
Я немного обновил Jade шаблоны и добавил форму входа.
Есть, однако, несколько моментов, пока не реализованных в текущей версии приложения:
- Документы ничего не знают о своем владельце
- Тесты работают неправильно, так как у меня появились проблемы при анализе того, как Expresso работает с сессиями
Со всем этим мы разберемся в следующих частях руководства.