Создайте (минимальное) приложение с полным стеком с аутентификацией пользователя через паспорт и 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
+jsonwebtoken
s для аутентификации и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
: глобальная функция, которая пригодится, когда мы будем использоватьvalidator
jsonwebtoken
: используется для авторизации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
время (в секундах) - В случае успеха добавьте токен в
Bearer
string (помните, в нашем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
и jsonwebtoken
s для аутентификации). Похлопайте себя по плечу за то, что вы продолжили. Бросьте также несколько хлопков. 👏
В части 2 (см. Ниже) мы создадим наш интерфейс, используя React
, будем использовать Redux
для управления состоянием и начнем использовать axios
для получения данных с нашего сервера.