Создайте (минимальное) приложение с полным стеком с аутентификацией пользователя через паспорт и JWT.


Мы создадим минимальное приложение для входа / авторизации с полным стеком, используя стек MERN (MongoDB для нашей базы данных, Express и Node для нашего внутреннего интерфейса и React для нашего внешнего интерфейса). Мы также интегрируем Redux для управления состоянием наших React компонентов.
Полную кодовую базу можно посмотреть здесь:
Наше приложение позволит пользователям
- регистр
- Авторизоваться
- Доступ к защищенным страницам, доступным только зарегистрированным пользователям
- Оставайтесь в системе, когда они закрывают приложение или обновляют страницу
- Выйти
Это должно стать прочной основой для создания более функционального полнофункционального приложения MERN. Ниже приведены примеры проектов, которые я создал с использованием этой базы mern-auth.
- Feeder (Github Repo и Среднее руководство): пакет npm для сбора отзывов от пользователей через встраиваемый виджет / компонент и панель администратора для управления проектами / просмотра отзывов.
- Команды (Github Repo): веб-приложение панели управления MERN и клон Asana
- Банковское веб-приложение с Plaid (средний учебник): средний учебник по созданию веб-приложения для личных финансов с использованием Plaid и MERN Stack.
Эта серия статей также должна позволить вам более эффективно создавать приложения с полным стеком, используя любой бэкэнд / интерфейс. Например, этот проект научил меня тому, что мне нужно было знать для создания IB Vine, который я построил с использованием Gatsby (фреймворк React.js), Redux и Firebase для бэкэнда / базы данных.
В этой части (часть 1) мы
- Инициализируем наш бэкэнд с помощью
npmи устанавливаем необходимые пакеты - Настройка базы данных MongoDB с помощью mLab
- Настройте сервер с
Node.jsиExpress - Создайте схему базы данных для определения
Userдля целей регистрации и входа в систему. - Настройте два маршрута API,
registerиlogin, используяpassport+jsonwebtokens для аутентификации иvalidatorдля проверки ввода - Протестируйте наши маршруты API с помощью Postman
Мы создадим серверную часть с нуля без стандартного кода, который, как мне кажется, лучше всего подходит для первого знакомства с MERN приложениями.
Опубликовано в блоге Bit
Bit помогает вам делиться и синхронизировать компоненты Javascript и UI в разных проектах и приложениях, чтобы быстрее создавать вместе с вашей командой. Это OSS, попробуйте.
Прежде чем мы начнем
Предварительные требования
У вас должно быть хотя бы базовое понимание фундаментальных концепций программирования и некоторый опыт работы с вводным _18 _ / _ 19 _ / _ 20_. Если у вас нет опыта работы с Javascript, но вы работали с Python, Ruby или другим подобным серверным языком, вы все равно сможете продолжить.
Этот пост не предназначен для объяснения MERN стека или используемых в нем технологий, но является хорошим введением в создание с его помощью приложения с полным стеком. Однако вы можете (и должны) узнать больше о технологиях, включенных в стек, прежде чем приступить к работе (Mongo, Express, React, Node).
Вот два хороших стартовых ресурса.
Стеки MEAN и MERN
Веб-приложения и нативные приложения созданы с использованием« стека различных технологий. Будучи студентом Flatiron, у меня есть… blog.cloudboost.io »
Установить
Наконец, убедитесь, что у вас установлено следующее.
- Текстовый редактор (Atom) (или VS code / Sublime Text)
- Последняя версия Node.js (мы будем использовать
npm, или Диспетчер пакетов узлов, для установки зависимостей - очень похоже наpipдля Python илиgemsдля Ruby) - MongoDB (быстрая установка: установите Homebrew и запустите
brew update && brew install mongodb) - Почтальон (для тестирования API)
- Prettier (для простого форматирования нашего Javascript; в Atom, Пакеты → Prettier → Toggle Format on Save для автоматического форматирования при сохранении)
Давайте начнем.
Часть 1: Создание нашего бэкэнда
я. Инициализация нашего проекта
Установите текущий каталог там, где вы хотите, чтобы ваш проект жил, и инициализируйте проект с помощью npm.
➜ ~ mkdir mern-auth ➜ ~ cd mern-auth ➜ mern-auth npm init
После выполнения команды утилита проведет вас через создание файла package.json.
Вы можете enter пройти через большинство из них безопасно, но продолжайте и установите entry point на server.js вместо значения по умолчанию index.js при появлении запроса (это можно сделать позже в нашем package.json).
ii. Настройка нашего package.json
1. Установите “main” точку входа на “server.js” вместо значения по умолчанию “index.js”, если вы еще этого не сделали (для обычных целей)
2. Установите следующие зависимости, используя npm
➜ mern-auth npm i bcryptjs body-parser concurrently express is-empty jsonwebtoken mongoose passport passport-jwt validator
Краткое описание каждого пакета и функции, которую он будет выполнять
bcryptjs: используется для хеширования паролей перед сохранением их в нашей базе данныхbody-parser: используется для анализа тел входящих запросов в промежуточном программном обеспеченииconcurrently: позволяет нам запускать наш бэкэнд и интерфейс одновременно и на разных портахexpress: находится поверхNode, чтобы упростить написание маршрутизации, обработки запросов и ответов.is-empty: глобальная функция, которая пригодится, когда мы будем использоватьvalidatorjsonwebtoken: используется для авторизацииmongoose: используется для взаимодействия с MongoDBpassport: используется для аутентификации запросов с помощью расширяемого набора подключаемых модулей, известных какstrategies.passport-jwt:passportстратегия аутентификации с помощью JSON Web Token (JWT); позволяет аутентифицировать конечные точки с помощью JWTvalidator: используется для проверки ввода (например, проверка правильности формата электронной почты, подтверждение совпадения паролей)
3. Установите следующую devDependency (-D), используя npm
➜ mern-auth npm i -D nodemon
Nodemon - это утилита, которая будет отслеживать любые изменения в вашем коде и автоматически перезагружать ваш сервер, что идеально подходит для разработки. Альтернативой может быть отключение вашего сервера (Ctrl+C) и его резервное копирование каждый раз, когда вы вносите изменения. Не идеально.
Обязательно используйте nodemon вместо node при запуске кода в целях разработки.
4. Измените объект “scripts” на следующий
"scripts": {
"start": "node server.js",
"server": "nodemon server.js",
},
Позже мы будем использовать nodemon run server для запуска нашего сервера разработки.
На этом этапе ваш package.json файл должен выглядеть следующим образом.
{
"name": "mern-auth",
"version": "1.0.0",
"description": "Mern Auth Example",
"main": "server.js",
"scripts": {
"start": "node server.js",
"server": "nodemon server.js"
},
"author": "",
"license": "MIT",
"dependencies": {
"bcryptjs": "^2.4.3",
"body-parser": "^1.18.3",
"concurrently": "^4.0.1",
"express": "^4.16.4",
"is-empty": "^1.2.0",
"jsonwebtoken": "^8.3.0",
"mongoose": "^5.3.11",
"passport": "^0.4.0",
"passport-jwt": "^4.0.0",
"validator": "^10.9.0"
}
}
iii. Настройка нашей базы данных
1. Перейдите в mLab и создайте аккаунт, если у вас его еще нет
2. Создайте новое развертывание MongoDB
Выберите AWS в качестве поставщика облачных услуг и Песочница в качестве типа тарифного плана. Затем установите свой регион AWS в зависимости от того, где вы живете. Наконец, назовите свою базу данных и отправьте заказ (не волнуйтесь, это бесплатно).
3. Перейдите в панель управления и нажмите на созданную вами базу данных.
Перейдите на вкладку Users, щелкните Add Database User и создайте пользователя базы данных. Вашей базе данных нужен как минимум один пользователь, чтобы использовать ее.
Найдите свой MongoDB URI; мы будем использовать это для подключения к нашей базе данных.
mongodb://<dbuser>:<dbpassword>@ds159993.mlab.com:59993/mern-auth
Замените <dbuser> и <dbpassword> учетными данными пользователя базы данных, которые вы только что создали.
4. Создайте config каталог, а в нем keys.js файл
➜ mern-auth mkdir config && cd config && touch keys.js
Внутри вашего keys.js файла разместите следующее для облегчения доступа за пределами этого файла.
module.exports = {
mongoURI: "YOUR_MONGOURI_HERE"
};
На этом пока все.
iv. Настройка нашего сервера с помощью Node.js и Express
Базовый процесс настройки нашего сервера выглядит следующим образом.
- Вставьте наши необходимые зависимости (а именно
express,mongooseиbodyParser) - Инициализируем наше приложение, используя
express() - Примените функцию промежуточного программного обеспечения для
bodyparser, чтобы мы могли ее использовать - Вытяните наш
MongoURIиз нашегоkeys.jsфайла и подключитесь к нашей базе данных MongoDB. - Установите
portдля работы нашего сервера и пусть наше приложение будет прослушивать этот порт.
Поместим следующее в наш server.js файл.
const express = require("express");
const mongoose = require("mongoose");
const bodyParser = require("body-parser");
const app = express();
// Bodyparser middleware
app.use(
bodyParser.urlencoded({
extended: false
})
);
app.use(bodyParser.json());
// DB Config
const db = require("./config/keys").mongoURI;
// Connect to MongoDB
mongoose
.connect(
db,
{ useNewUrlParser: true }
)
.then(() => console.log("MongoDB successfully connected"))
.catch(err => console.log(err));
const port = process.env.PORT || 5000; // process.env.port is Heroku's port if you choose to deploy the app there
app.listen(port, () => console.log(`Server up and running on port ${port} !`));
Запустите nodemon run server, и должно получиться следующее.
➜ mern-auth nodemon run server [nodemon] 1.18.3 [nodemon] to restart at any time, enter `rs` [nodemon] watching: *.* [nodemon] starting `node run server server.js` Server up and running on port 5000 ! MongoDB successfully connected
Попробуйте изменить сообщение "Server up and running..." в вашем файле, нажмите «Сохранить», и вы увидите, что ваш сервер автоматически перезагружается.
Поздравляю! Вы настроили сервер с использованием NodeJS и Express и успешно подключились к своей базе данных MongoDB.
v. Настройка нашей схемы базы данных
Давайте создадим папку models для определения нашей пользовательской схемы. В models создайте файл User.js.
➜ mern-auth mkdir models && cd models && touch User.js
В течение User.js мы будем
- Вставьте наши необходимые зависимости
- Создайте схему для представления пользователя, определяя поля и типы как объекты схемы.
- Экспортируйте модель, чтобы мы могли получить к ней доступ вне этого файла.
Поместим следующее в наш User.js файл.
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
// Create Schema
const UserSchema = new Schema({
name: {
type: String,
required: true
},
email: {
type: String,
required: true
},
password: {
type: String,
required: true
},
date: {
type: Date,
default: Date.now
}
});
module.exports = User = mongoose.model("users", UserSchema);
Довольно стандартная настройка для того, что вы ожидаете от пользователя.
vi. Настройка проверки формы
Прежде чем настраивать маршруты, давайте создадим каталог для проверки ввода и создадим файлы register.js и login.js для проверки каждого маршрута.
➜ mern-auth mkdir validation && cd validation && touch register.js login.js
Наша процедура проверки для нашего файла register.js будет выглядеть следующим образом:
- Вставьте зависимости
validatorиis-empty - Экспортируйте функцию
validateRegisterInput, которая принимаетdataв качестве параметра (отправляется из нашей формы регистрации во внешнем интерфейсе, которую мы создадим во второй части). - Создайте экземпляр нашего объекта
errors - Преобразуйте все пустые поля в пустую строку перед запуском проверок (
validatorработает только со строками) - Проверьте наличие пустых полей, допустимые форматы электронной почты, требования к паролю и подтвердите равенство паролей с помощью
validatorфункций. - Вернуть наш объект
errorsс любыми и всеми ошибками, а также логическимisValid, которое проверяет, есть ли у нас какие-либо ошибки.
Поместим следующее в register.js.
const Validator = require("validator");
const isEmpty = require("is-empty");
module.exports = function validateRegisterInput(data) {
let errors = {};
// Convert empty fields to an empty string so we can use validator functions
data.name = !isEmpty(data.name) ? data.name : "";
data.email = !isEmpty(data.email) ? data.email : "";
data.password = !isEmpty(data.password) ? data.password : "";
data.password2 = !isEmpty(data.password2) ? data.password2 : "";
// Name checks
if (Validator.isEmpty(data.name)) {
errors.name = "Name field is required";
}
// Email checks
if (Validator.isEmpty(data.email)) {
errors.email = "Email field is required";
} else if (!Validator.isEmail(data.email)) {
errors.email = "Email is invalid";
}
// Password checks
if (Validator.isEmpty(data.password)) {
errors.password = "Password field is required";
}
if (Validator.isEmpty(data.password2)) {
errors.password2 = "Confirm password field is required";
}
if (!Validator.isLength(data.password, { min: 6, max: 30 })) {
errors.password = "Password must be at least 6 characters";
}
if (!Validator.equals(data.password, data.password2)) {
errors.password2 = "Passwords must match";
}
return {
errors,
isValid: isEmpty(errors)
};
};
Наша проверка для нашего login.js осуществляется в том же порядке, что и выше, хотя и с другими полями.
const Validator = require("validator");
const isEmpty = require("is-empty");
module.exports = function validateLoginInput(data) {
let errors = {};
// Convert empty fields to an empty string so we can use validator functions
data.email = !isEmpty(data.email) ? data.email : "";
data.password = !isEmpty(data.password) ? data.password : "";
// Email checks
if (Validator.isEmpty(data.email)) {
errors.email = "Email field is required";
} else if (!Validator.isEmail(data.email)) {
errors.email = "Email is invalid";
}
// Password checks
if (Validator.isEmpty(data.password)) {
errors.password = "Password field is required";
}
return {
errors,
isValid: isEmpty(errors)
};
};
vii. Настройка наших маршрутов API
Теперь, когда мы прошли проверку, давайте создадим новую папку для наших маршрутов api и создадим users.js файл для регистрации и входа в систему.
➜ mern-auth mkdir routes && cd routes && mkdir api && cd api && touch users.js
Вверху users.js давайте извлечем наши необходимые зависимости и загрузим наши проверки ввода и модель пользователя.
const express = require("express");
const router = express.Router();
const bcrypt = require("bcryptjs");
const jwt = require("jsonwebtoken");
const keys = require("../../config/keys");
// Load input validation
const validateRegisterInput = require("../../validation/register");
const validateLoginInput = require("../../validation/login");
// Load User model
const User = require("../../models/User");
Создайте конечную точку регистрации
Для нашей конечной точки регистрации мы будем
- Вытяните переменные
errorsиisValidиз нашей функцииvalidateRegisterInput(req.body)и проверьте правильность ввода - Если введенные данные действительны, используйте
User.findOne()MongoDB, чтобы узнать, существует ли уже пользователь. - Если пользователь является новым пользователем, заполните поля (
name,email,password) данными, отправленными в теле запроса. - Используйте
bcryptjsдля хеширования пароля перед сохранением его в базе данных
Поместим следующее в наш users.js файл для нашего маршрута регистрации.
// @route POST api/users/register // @desc Register user // @access Public router.post("/register", (req, res) => { // Form validation const { errors, isValid } = validateRegisterInput(req.body); // Check validation if (!isValid) { return res.status(400).json(errors); } User.findOne({ email: req.body.email }).then(user => { if (user) { return res.status(400).json({ email: "Email already exists" }); } else { const newUser = new User({ name: req.body.name, email: req.body.email, password: req.body.password }); // Hash password before saving in database bcrypt.genSalt(10, (err, salt) => { bcrypt.hash(newUser.password, salt, (err, hash) => { if (err) throw err; newUser.password = hash; newUser .save() .then(user => res.json(user)) .catch(err => console.log(err)); }); }); } }); });
Настроить паспорт
В каталоге config создайте файл passport.js.
➜ mern-auth cd config && touch passport.js
Прежде чем настраивать паспорт, давайте добавим следующее в наш keys.js файл.
module.exports = {
mongoURI: "YOUR_MONGOURI_HERE",
secretOrKey: "secret"
};
Вернуться к passport.js. Вы можете узнать больше о стратегии passport-jwt по ссылке ниже. Он отлично разбирается в том, как построена стратегия аутентификации JWT, объясняя необходимые параметры, переменные и функции, такие как options, secretOrKey, jwtFromRequest, verify и jwt_payload.
Поместим следующее в наш passport.js файл.
const JwtStrategy = require("passport-jwt").Strategy;
const ExtractJwt = require("passport-jwt").ExtractJwt;
const mongoose = require("mongoose");
const User = mongoose.model("users");
const keys = require("../config/keys");
const opts = {};
opts.jwtFromRequest = ExtractJwt.fromAuthHeaderAsBearerToken();
opts.secretOrKey = keys.secretOrKey;
module.exports = passport => {
passport.use(
new JwtStrategy(opts, (jwt_payload, done) => {
User.findById(jwt_payload.id)
.then(user => {
if (user) {
return done(null, user);
}
return done(null, false);
})
.catch(err => console.log(err));
})
);
};
Также обратите внимание, что jwt_payload будет отправлен через нашу конечную точку входа ниже.
Создайте конечную точку входа
Для нашей конечной точки входа мы будем
- Вытяните переменные
errorsиisValidиз нашей функцииvalidateLoginInput(req.body)и проверьте правильность ввода - Если введенные данные действительны, используйте
User.findOne()MongoDB, чтобы узнать, существует ли пользователь. - Если пользователь существует, используйте
bcryptjsдля сравнения отправленного пароля с хешированным паролем в нашей базе данных. - Если пароли совпадают, создаем наш
JWT Payload - Подпишите наш
jwt, включая нашpayload,keys.secretOrKeyизkeys.js, и установивexpiresInвремя (в секундах) - В случае успеха добавьте токен в
Bearerstring (помните, в нашемpassport.jsфайле мы установилиopts.jwtFromRequest = ExtractJwt.fromAuthHeaderAsBearerToken();)
Поместим следующее в наш users.js файл для нашего маршрута входа.
// @route POST api/users/login // @desc Login user and return JWT token // @access Public router.post("/login", (req, res) => { // Form validation const { errors, isValid } = validateLoginInput(req.body); // Check validation if (!isValid) { return res.status(400).json(errors); } const email = req.body.email; const password = req.body.password; // Find user by email User.findOne({ email }).then(user => { // Check if user exists if (!user) { return res.status(404).json({ emailnotfound: "Email not found" }); } // Check password bcrypt.compare(password, user.password).then(isMatch => { if (isMatch) { // User matched // Create JWT Payload const payload = { id: user.id, name: user.name }; // Sign token jwt.sign( payload, keys.secretOrKey, { expiresIn: 31556926 // 1 year in seconds }, (err, token) => { res.json({ success: true, token: "Bearer " + token }); } ); } else { return res .status(400) .json({ passwordincorrect: "Password incorrect" }); } }); }); });
Не забудьте экспортировать наш маршрутизатор в конец users.js, чтобы мы могли использовать его где-нибудь еще.
module.exports = router;
Добавление наших маршрутов в наш server.js файл
Внесите в server.js следующие полужирные добавления.
const express = require("express");
const mongoose = require("mongoose");
const bodyParser = require("body-parser");
const passport = require("passport");
const users = require("./routes/api/users");
const app = express();
// Bodyparser middleware
app.use(
bodyParser.urlencoded({
extended: false
})
);
app.use(bodyParser.json());
// DB Config
const db = require("./config/keys").mongoURI;
// Connect to MongoDB
mongoose
.connect(
db,
{ useNewUrlParser: true }
)
.then(() => console.log("MongoDB successfully connected"))
.catch(err => console.log(err));
// Passport middleware
app.use(passport.initialize());
// Passport config
require("./config/passport")(passport);
// Routes
app.use("/api/users", users);
const port = process.env.PORT || 5000;
app.listen(port, () => console.log(`Server up and running on port ${port} !`));
viii. Тестирование наших маршрутов API с помощью Postman
Тестирование нашей конечной точки регистрации
Откройте Почтальон и
- Установите тип запроса
POST - Установите URL-адрес запроса на
http://localhost:5000/api/users/register - Перейдите на вкладку
Body, выберитеx-www-form-urlencoded, введите параметры регистрации и нажмитеSend.
Вы должны получить ответ HTTP о состоянии 200 OK, а новый пользователь должен быть возвращен как JSON.

Проверьте свою базу данных на mLab, и вы должны увидеть нового пользователя, созданного с указанными выше учетными данными.
Тестирование нашей конечной точки входа
Как и в предыдущем случае, в Postman
- Установите тип запроса
POST - Установите URL-адрес запроса на
http://localhost:5000/api/users/login - Перейдите на вкладку
Body, выберитеx-www-form-urlencoded, введите параметры входа и нажмитеSend.
Вы должны получить ответ о состоянии HTTP 200 OK и получить jwt в ответе.

Ошибки тестирования
Вам следует поэкспериментировать и проверить свои validator ошибки, поигравшись с различными случаями, когда пользователь регистрируется и входит в систему (например, неверные форматы электронной почты, несоответствующие пароли). При тестировании API в Postman вы должны увидеть возвращенный объект ошибок.

В конечном итоге мы внесем эти ошибки в наш интерфейс и отобразим сообщения в самой форме.
На этом наш бэкэнд!
Мы успешно настроили и протестировали наши маршруты API (с использованием passport и jsonwebtokens для аутентификации). Похлопайте себя по плечу за то, что вы продолжили. Бросьте также несколько хлопков. 👏
В части 2 (см. Ниже) мы создадим наш интерфейс, используя React, будем использовать Redux для управления состоянием и начнем использовать axios для получения данных с нашего сервера.