Нет сомнений в том, что почти все приложения связаны с хранением данных. Но сам фреймворк Express может сохранять данные только через программные переменные, он не обеспечивает персистентность данных. Просто сохранение данных в памяти не справляется с реальными сценариями. Потому что сама память не подходит для хранения больших объемов данных, и данные исчезнут при остановке службы. Хотя мы также можем сохранять данные в виде файлов, очевидно, что данные в файлах не подходят для операций запросов. Все, далее мы научимся сохранять данные в виде базы данных MongoDB в Express.
Основное содержание, включенное в эту статью:
- Как работает MongoDB.
- Как использовать Мангуст.
- Как безопасно создавать учетные записи пользователей.
- Как использовать пароль пользователя для авторизованных операций.
Почему MongoDB?
Для веб-приложений выбор базы данных обычно можно разделить на две категории: реляционная и нереляционная. Среди них первый имеет преимущества типовых электронных таблиц, данные которых структурированы и сопровождаются строгими правилами. Типичные реляционные базы данных включают: MySQL, SQL Server и PostgreSQL. Последнюю также обычно называют базой данных NoSQL, и ее структура относительно более гибкая, что очень похоже на JS.
Но почему разработчики Node особенно любят базу данных Mongo в NoSQL, которая также сформировала популярный стек технологий MEAN?
Первая причина: Mongo — самый популярный тип данных NoSQL. Это также делает информацию о Могоне в Интернете очень богатой, и все ямы, с которыми вы можете столкнуться в процессе фактического использования, могут найти ответ. И как зрелый проект, Mongo также был признан и применен крупными компаниями.
Другая причина в том, что сам Mongo очень надежен и самобытен. Он использует высокопроизводительный C++ для базовой реализации, которая также завоевала доверие большого числа пользователей.
Хотя Mongo не реализован в JavaScript, собственная оболочка использует язык JavaScript. Это означает, что Mongo можно управлять из консоли с помощью JavaScript. Кроме того, это снижает стоимость изучения нового языка для разработчиков Node.
Конечно, Mongo не является правильным выбором для всех приложений Express, и реляционные базы данных по-прежнему занимают очень важное место. Кстати, CouchDB в NoSQL тоже очень мощный.
Примечание. Хотя в этой статье речь пойдет только об использовании Mongo и библиотеки классов Mongoose. Но если вы так же знакомы с SQL, как и я, и хотите использовать реляционную базу данных с Express, вы можете проверить это.Sequelize. Он обеспечивает хорошую поддержку многих реляционных баз данных.
Как работает Монго
Прежде чем официально использовать Mongo, давайте посмотрим, как работает Mongo.
Для большинства приложений база данных, такая как Mongo, используется на сервере для сохраняемости. Хотя в приложении можно создать несколько баз данных, в большинстве случаев будет использоваться только одна.
Если вы хотите получить доступ к этим базам данных в обычном режиме, сначала вам нужно запустить службу Mongo. Клиент выполняет различные операции с базой данных, отправляя инструкции серверу. Программы, которые соединяют клиент и сервер, обычно называются драйверами баз данных. Для базы данных Mongo ее драйвером базы данных в среде Node является Mongoose.
Каждая база данных будет иметь одну или несколько коллекций данных, похожих на массивы. Например, простое приложение для блога может иметь набор статей и набор пользователей. Но эти коллекции данных гораздо мощнее, чем массивы. Например, вы можете запросить коллекцию для пользователей старше 18 лет.
Каждая коллекция хранит документы в формате JSON, хотя технически это не JSON. Каждый документ соответствует записи, и каждая запись содержит несколько атрибутов поля. Кроме того, записи документов в одной и той же коллекции не обязательно имеют одинаковые свойства полей. Это также одно из самых больших различий между NoSQL и реляционными базами данных.
На самом деле, технически документ находится в двоичном формате JSON, сокращенно BSON. В реальном процессе написания кода мы не будем работать с BSON напрямую. В большинстве случаев он будет преобразован в объект JavaScript. Кроме того, BSON кодируется и декодируется иначе, чем JSON. BSON также поддерживает больше типов, например, дату, отметку времени. На следующем рисунке показана структура использования базы данных в приложении:
И последнее, но не менее важное: Mongo добавляет атрибут _id к каждой записи документа, что делает запись уникальной. Две записи документов одного типа считаются одной и той же записью, если их атрибуты id совпадают.
Проблемы, о которых должны знать пользователи SQL
Если у вас есть опыт работы с реляционными базами данных, вы обнаружите, что многие понятия в Mongo соответствуют значению SQL.
Прежде всего, концепция документа в Mongo фактически эквивалентна строке записей в SQL. В пользовательской системе приложения каждый пользователь является документом в Mongo и записью в SQL. Но, в отличие от SQL, в Mongo нет обязательной схемы на уровне базы данных, поэтому запись пользователя без имени пользователя и адреса электронной почты в Mongo разрешена.
Во-вторых, коллекции в Mongo соответствуют таблицам в SQL, и обе они используются для хранения записей одного и того же типа.
Точно так же базы данных в Mongo очень похожи на концепции баз данных SQL. Обычно приложение имеет только одну базу данных, и база данных может содержать несколько коллекций или таблиц данных.
Для получения дополнительных таблиц соответствия терминов вы можете перейти к официальнойДокументация.
Настройка среды Mongo
Прежде чем его использовать, первая задача, конечно же, установить базу данных Mongo на машину и подтянуть сервис. Если на вашем компьютере установлена macOS и вам не нравится режим командной строки, вы можете установить его с помощьюMongo.appПриложение завершает построение среды. Если вы знакомы с взаимодействием с командной строкой, вы можете установить его с помощью команды Homebrew brew install mongodb.
Система Ubuntu может относиться кДокументация, в то время как Debian может ссылаться наДокументацияВыполните установку Mongo.
Кроме того, в этой книге мы будем предполагать, что ваша установка использует конфигурацию базы данных Mongo по умолчанию. То есть вы не меняли номер сервисного порта Mongo, а использовали значение по умолчанию 27017.
Используйте Mongoose для работы с базой данных Mongo
После установки Mongo следующий вопрос — как работать с базой данных в среде Node. Лучше всего использовать официальныйMongooseбиблиотека классов. Его официальная документация описывает это как:
Mongoose предоставляет интуитивно понятное и основанное на схеме решение общих проблем с базами данных, таких как моделирование данных, преобразование типов и проверка данных программ.
Другими словами, Mongoose предлагает гораздо больше, чем просто мост между Node и Mongo. Далее давайте познакомимся с возможностями Mongoose, создав простой веб-сайт с пользовательской системой.
Готов к работе
Чтобы лучше изучить содержание этой статьи, ниже мы разработаем простое социальное приложение. Приложение будет реализовывать такие функции, как регистрация пользователя, редактирование личной информации и просмотр чужой информации. Здесь мы называем это «Узнай обо мне» или сокращенно LAM. Приложение в основном включает следующие страницы:
- Домашняя страница, на которой перечислены все пользователи и можно щелкнуть, чтобы просмотреть сведения о пользователе.
- Страница личной информации, используемая для отображения такой информации, как имя пользователя.
- Страница регистрации пользователя.
- Страница входа пользователя.
Как и раньше, сначала нам нужно создать новый каталог проекта и отредактироватьpackage.jsonИнформация в файле:
{
"name": "learn-about-me",
"private": true,
"scripts": {
"start": "node app"
},
"dependencies": {
"bcrypt-nodejs": "0.0.3",
"body-parser": "^1.6.5",
"connect-flash": "^0.1.1",
"cookie-parser": "^1.3.2",
"ejs": "^1.0.0",
"express": "^4.0.0",
"express-session": "^1.7.6",
"mongoose": "^3.8.15",
"passport": "^0.2.0",
"passport-local": "^1.0.0"
}
}
Далее запуститеnpm installУстановите эти зависимости. Функции этих зависимостей будут представлены одна за другой в следующем содержании.
Следует отметить, что здесь мы вводим модуль шифрования, реализованный на чистом JS.bcrypt-nodejs. На самом деле, в npm также есть модуль шифрования, реализованный на языке C.bcrypt. несмотря на то чтоbcryptПроизводительность лучше, но из-за необходимости компилировать код C все установки неbcrypt-nodejsПростой. Однако функции этих двух библиотек классов согласованы и могут свободно переключаться.
Создайте модель пользователя
Как упоминалось ранее, Mongo хранит данные в виде BSON. Например, представление Hello World в BSON:
\x16\x00\x00\x00\x02hello\x00\x06\x00\x00\x00world\x00\x00
Хотя компьютеры полностью понимают формат BSON, ясно, что BSON не является удобным для чтения форматом для людей. В результате разработчики изобретают концепцию модели базы данных, которую легче понять. Модель базы данных определяет объекты базы данных способом, приближенным к человеческому языку. Модель представляет запись базы данных и обычно объект на языке программирования. Например, здесь он представляет объект JavaScript.
Помимо представления записи в базе данных, модель обычно сопровождается такими методами, как проверка данных и расширение данных. Давайте рассмотрим эти функции в Mongoose на конкретных примерах.
В примере мы создадим пользовательскую модель со следующими свойствами:
- Имя пользователя, этот атрибут не может быть установлен по умолчанию и должен быть уникальным.
- Пароль также не может быть установлен по умолчанию.
- Время создания.
- Псевдоним пользователя, используемый для отображения информации и необязательный.
- Личный профиль, необязательные атрибуты.
В Mongoose мы используем схему для определения модели пользователя. В дополнение к включению свойств, описанных выше, позже к нему будут добавлены некоторые методы типов. Создается в корневом каталоге проектаmodelsпапку, затем создайте файл с именемuser.jsфайл и скопируйте следующий код:
var mongoose = require("mongoose");
var userSchema = mongoose.Schema({
username: { type: String, require: true, unique: true },
password: { type: String, require: true },
createdAt: {type: Date, default: Date.now },
displayName: String,
bio: String
});
Из приведенного выше кода видно, что определение поля атрибута очень простое. В то же время мы также согласовали тип данных, уникальность, значение по умолчанию и значение поля по умолчанию.
После определения модели следующим шагом является определение методов в модели. Во-первых, давайте добавим простой метод, который возвращает имя пользователя. Возвращает псевдоним, если пользователь определяет псевдоним, в противном случае возвращает имя пользователя напрямую. код показывает, как показано ниже:
...
userSchema.methods.name = function() {
return this.displayName || this.username;
}
Точно так же для обеспечения безопасности информации о пользователе в базе данных поле пароля должно храниться в зашифрованном виде. Таким образом, даже в случае утечки или вторжения в базу данных, можно в определенной степени обеспечить безопасность пользовательской информации. Здесь мы будем использоватьBcryptПрограмма хэширует пароль пользователя с помощью одностороннего хеширования, а затем сохраняет зашифрованный результат в базе данных.
Во-первых, нам нужноuser.jsимпорт заголовка файлаBcryptбиблиотека классов. В процессе использования мы можем повысить безопасность данных, увеличив количество хэшей. Конечно, хэш-операции — это очень операции, поэтому мы должны выбрать относительно умеренное значение. Например, в приведенном ниже коде мы устанавливаем количество хэшей равным 10.
var bcrypt = require("bcrypt-nodejs");
var SALT_FACTOR = 10;
Конечно, хеширование пароля должно быть выполнено перед сохранением данных. Таким образом, эта часть кода должна быть выполнена в функции обратного вызова до сохранения данных, код выглядит следующим образом:
...
var noop = function() {};
// 保存操作之前的回调函数
userSchema.pre("save", function(done) {
var user = this;
if (!user.isModified("password")) {
return done();
}
bcrypt.genSalt(SALT_FACTOR, function(err, salt) {
if (err) {
return done(err);
}
bcrypt.hash(user.password, salt, noop,
function(err, hashedPassword) {
if (err) {
return done(err);
}
user.password = hashedPassword;
done();
}
);
});
});
Эта функция обратного вызова вызывается перед каждым сохранением базы данных, поэтому она гарантирует, что ваш пароль будет сохранен в зашифрованном виде.
Помимо необходимости шифрования паролей, еще одним распространенным требованием является проверка авторизации пользователя. Например, операция аутентификации по паролю во время операции входа пользователя.
...
userSchema.methods.checkPassword = function(guess, done) {
bcrypt.compare(guess, this.password, function(err, isMatch) {
done(err, isMatch);
});
}
Из соображений безопасности здесь мы используемbcrypt.compareфункция вместо простой проверки на равенство === .
После завершения определения модели и реализации общего метода нам нужно предоставить ее для использования другим кодом. Однако представить модель очень просто, всего двумя строками кода:
...
var User = mongoose.model("User", userSchema);
module.exports = User;
models/user.jsПолный код в файле выглядит следующим образом:
// 代码清单 8.8 models/user.js编写完成之后
var bcrypt = require("bcrypt-nodejs");
var SALT_FACTOR = 10;
var mongoose = require("mongoose");
var userSchema = mongose.Schema({
username: { type: String, require: true, unique: true },
password: { type: String, require: true },
createdAt: {type: Date, default: Date.now },
displayName: String,
bio: String
});
userSchema.methods.name = function() {
return this.displayName || this.username;
}
var noop = function() {};
userSchema.pre("save", function(done) {
var user = this;
if (!user.isModified("password")) {
return done();
}
bcrypt.genSalt(SALT_FACTOR, function(err, salt) {
if (err) { return done(err); }
bcrypt.hash(user.password, salt, noop,
function(err, hashedPassword) {
if (err) { return done(err); }
user.password = hashedPassword;
done();
}
);
});
});
userSchema.methods.checkPassword = function(guess, done) {
bcrypt.compare(guess, this.password, function(err, isMatch) {
done(err, isMatch);
});
}
var User = mongoose.model("User", userSchema);
module.exports = User;
использование модели
После того, как модель определена, следующим шагом будет ее использование на домашней странице, странице редактирования, регистрации и других страницах. По сравнению с предыдущим определением модели процесс использования относительно проще.
Сначала создайте основной файл записи в корневом каталоге проекта.app.jsи скопируйте код ниже:
var express = require("express");
var mongoose = require("mongoose");
var path = require("path");
var bodyParser = require("body-parser");
var cookieParser = require("cookie-parser");
var session = require("express-session");
var flash = require("connect-flash");
var routes = require("./routes");
var app = express();
// 连接到你MongoDB服务器的test数据库
mongoose.connect("mongodb://localhost:27017/test");
app.set("port", process.env.PORT || 3000);
app.set("views", path.join(__dirname, "views"));
app.set("view engine", "ejs");
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(session({
secret: "TKRv0IJs=HYqrvagQ#&!F!%V]Ww/4KiVs$s,<<MX",
resave: true,
saveUninitialized: true
}));
app.use(flash());
app.use(routes);
app.listen(app.get("port"), function() {
console.log("Server started on port " + app.get("port"));
});
Далее нам нужно реализовать промежуточное программное обеспечение маршрутизации, использованное выше. Новое в корневом каталогеroutes.jsи скопируйте код:
var express = require("express");
var User = require("./models/user");
var router = express.Router();
router.use(function(req, res, next) {
res.locals.currentUser = req.user;
res.locals.errors = req.flash("error");
res.locals.infos = req.flash("info");
next();
});
router.get("/", function(req, res, next) {
User.find()
.sort({ createdAt: "descending" })
.exec(function(err, users) {
if (err) { return next(err); }
res.render("index", { users: users });
});
});
module.exports = router;
В этих двух фрагментах кода, во-первых, мы используем Mongoose для подключения к базе данных. Затем в промежуточном программном обеспечении маршрутизации черезUser.findАсинхронно извлекает список пользователей и передает его в шаблон представления главной страницы.
Далее переходим к реализации домашнего вида. Сначала создайте в корневом каталогеviewsпапку, затем добавьте первый файл шаблона в папку_header.ejs:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Learn About Me</title>
<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css">
</head>
<body>
<div class="navbar navbar-default navbar-static-top" role="navigation">
<div class="container">
<div class="navbar-header">
<a class="navbar-brand" href="/">Learn About Me</a>
</div>
<!--
如果用户已经登陆了则对导航条进行相应的改变。
一开始你的代码中并不存在currentUser,所以总会显示一个状态
-->
<ul class="nav navbar-nav navbar-right">
<% if (currentUser) { %>
<li>
<a href="/edit">
Hello, <%= currentUser.name() %>
</a>
</li>
<li><a href="/logout">Log out</a></li>
<% } else { %>
<li><a href="/login">Log in</a></li>
<li><a href="/signup">Sign up</a></li>
<% } %>
</ul>
</div>
</div>
<div class="container">
<% errors.forEach(function(error) { %>
<div class="alert alert-danger" role="alert">
<%= error %>
</div>
<% }) %>
<% infos.forEach(function(info) { %>
<div class="alert alert-info" role="alert">
<%= info %>
</div>
<% }) %>
Вы могли заметить, что имена этих файлов начинаются со знака подчеркивания. Это соглашение сообщества, и все шаблоны компонентов подчеркнуты.
Затем добавьте второй универсальный шаблон компонента._footer.js:
</div>
</body>
</html>
Наконец, мы добавляем файл шаблона представления домашней страницы. Шаблон представления примет входящее промежуточное ПО.usersпеременная и закончить рендеринг:
<% include _header %>
<h1>Welcome to Learn About Me!</h1>
<% users.forEach(function(user) { %>
<div class="panel panel-default">
<div class="panel-heading">
<a href="/users/<%= user.username %>">
<%= user.name() %>
</a>
</div>
<% if (user.bio) { %>
<div class="panel-body"><%= user.bio %></div>
<% } %>
</div>
<% }) %>
<% include _footer %>
Убедившись, что код правильный, запустите службу базы данных Mongo и используйтеnpm startПодтяните проект. Затем доступ через браузерlocalhost:3000Вы можете ввести интерфейс домашней страницы, как показано ниже:
Конечно, поскольку в настоящее время в базе данных нет записей, здесь нет информации о пользователе.
Далее мы реализуем функции регистрации пользователя и входа в систему. Но перед этим нам нужноapp.jsвведен вbody-parserМодуль используется для последующего разбора параметров запроса.
var bodyParser = require("body-parser");
...
app.use(bodyParser.urlencoded({ extended: false }));
…
Для повышения безопасности здесь мы будемbody-parserДля расширения модуля установлено значение false . Далее мыroutes.jsДобавить кsign-upФункциональный обработчик промежуточного ПО:
var passport = require("passport");
...
router.get("/signup", function(req, res) {
res.render("signup");
});
router.post("/signup", function(req, res, next) {
// 参数解析
var username = req.body.username;
var password = req.body.password;
// 调用findOne只返回一个用户。你想在这匹配一个用户名
User.findOne({ username: username }, function(err, user) {
if (err) { return next(err); }
// 判断用户是否存在
if (user) {
req.flash("error", "User already exists");
return res.redirect("/signup");
}
// 新建用户
var newUser = new User({
username: username,
password: password
});
// 插入记录
newUser.save(next);
});
// 进行登录操作并实现重定向
}, passport.authenticate("login", {
successRedirect: "/",
failureRedirect: "/signup",
failureFlash: true
}));
После определения промежуточного программного обеспечения маршрутизации давайте реализуем шаблон представления.signup.ejsдокумент.
// 拷贝代码到 views/signup.ejs
<% include _header %>
<h1>Sign up</h1>
<form action="/signup" method="post">
<input name="username" type="text" class="form-control" placeholder="Username" required autofocus>
<input name="password" type="password" class="form-control" placeholder="Password" required>
<input type="submit" value="Sign up" class="btn btn-primary btn-block">
</form>
<% include _footer %>
Если вы успешно создадите пользователя и снова посетите домашнюю страницу, вы увидите список пользователей:
Интерфейс страницы регистрации примерно такой:
Прежде чем реализовать функцию входа в систему, мы сначала завершаем функцию отображения личной информации. существуетroutes.jsДобавьте следующую промежуточную функцию:
...
router.get("/users/:username", function(req, res, next) {
User.findOne({ username: req.params.username }, function(err, user) {
if (err) { return next(err); }
if (!user) { return next(404); }
res.render("profile", { user: user });
});
});
...
Затем напишите файл шаблона представленияprofile.ejs:
// 保存到 views 文件夹中
<% include _header %>
<!--
参考变量currentUser来判断你的登陆状态。不过现在它总会是false状态
-->
<% if ((currentUser) && (currentUser.id === user.id)) { %>
<a href="/edit" class="pull-right">Edit your profile</a>
<% } %>
<h1><%= user.name() %></h1>
<h2>Joined on <%= user.createdAt %></h2>
<% if (user.bio) { %>
<p><%= user.bio %></p>
<% } %>
<% include _footer %>
Если теперь вы войдете на страницу сведений о пользователе через домашнюю страницу, у вас будет интерфейс, подобный следующему изображению:
Аутентификация пользователя по паспорту
В дополнение к вышеперечисленным основным функциям важными функциями модели User являются вход в систему и аутентификация авторизации. И в этом самая большая разница между моделью User и другими моделями. Итак, следующая задача — реализовать страницу входа и выполнить аутентификацию по паролю и разрешению.
Чтобы уменьшить ненужную нагрузку, мы будем использовать сторонний модуль Passport. Этот шаблон представляет собой ПО промежуточного слоя Node, специально разработанное для обработки запросов на проверку. С помощью этого промежуточного программного обеспечения сложные операции аутентификации могут быть реализованы с помощью небольшого фрагмента кода. Однако Passport не указывает, как аутентифицировать пользователей, он просто предоставляет некоторые модульные функции.
Настроить паспорт
В процессе настройки Passport есть три основных момента:
- Настройте промежуточное ПО Passport.
- Задает операции сериализации и десериализации Passport для модели User.
- Сообщите Passport, как аутентифицировать пользователя.
Во-первых, при инициализации окружения Passport нужно внедрить в проект какое-то другое промежуточное ПО. Они есть:
- body-parser
- cookie-parser
- express-session
- connect-flash
- passport.initialize
- passport.session
Первые четыре промежуточного программного обеспечения уже представлены. Их функции: body-parser для анализа параметров, cookie-parser для обработки файлов cookie, полученных из браузера, express-session для обработки пользовательских сессий и connect-flash для отображения пользователями сообщений об ошибках.
Наконец, нам нужноapp.jsПредставьте модуль Passport и позже вызовите в нем две функции промежуточного программного обеспечения.
var bodyParser = require("body-parser");
var cookieParser = require("cookie-parser");
var flash = require("connect-flash");
var passport = require("passport");
var session = require("express-session");
...
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(session({
// 需要一串随机字母序列,字符串不一定需要跟此处一样
secret: "TKRv0IJs=HYqrvagQ#&!F!%V]Ww/4KiVs$s,<<MX",
resave: true,
saveUninitialized: true
}));
app.use(flash());
app.use(passport.initialize());
app.use(passport.session());
...
В коде мы используем случайную строку для кодирования сеанса клиента. Это может в определенной степени повысить безопасность файлов cookie. и воляresaveУстановить какtrueЭто гарантирует, что даже если сеанс не был изменен, он все равно будет обновлен.
Следующим шагом является второй шаг: установите сериализацию и десериализацию модели User с помощью Passport. Таким образом, Passport может реализовать взаимное преобразование сеансовых и пользовательских объектов. Документация Passport описывает эту операцию как:
В стандартном веб-приложении аутентификация пользователя требуется только тогда, когда клиент отправляет запрос на вход. Если аутентификация пройдена, между ними будет создан новый сеанс, который будет сохранен в файле cookie для обслуживания. Любые последующие операции больше не будут выполнять операцию аутентификации, вместо этого будет использоваться сеанс, однозначно указанный в файле cookie. Следовательно, в Passport необходимо реализовать взаимное преобразование сеансовых и пользовательских объектов посредством сериализации и десериализации.
Для удобства дальнейшего обслуживания кода здесь мы создаем новый с именемsetuppassport.jsфайл и поместите в него код сериализации и десериализации. Наконец, мы представляем егоapp.jsсередина:
…
var setUpPassport = require("./setuppassport");
…
var app = express();
mongoose.connect("mongodb://localhost:27017/test");
setUpPassport();
…
Нижеsetuppassport.jsКод внутри реализован. Поскольку объекты User имеютidСвойство используется как уникальный идентификатор, поэтому мы используем его для сериализации и десериализации объекта User:
// setuppassport.js 文件中的代码
var passport = require("passport");
var User = require("./models/user");
module.exports = function() {
passport.serializeUser(function(user, done) {
done(null, user._id);
});
passport.deserializeUser(function(id, done) {
User.findById(id, function(err, user) {
done(err, user);
});
});
}
Далее идет самая сложная часть, как аутентифицироваться?
Перед запуском аутентификации осталось проделать небольшую работу: задать политику аутентификации. Хотя Passport поставляется с Facebook, стратегией аутентификации Google, здесь нам нужно установитьlocal strategy. Потому что правила и код проверочной части реализованы нами самими.
Во-первых, мыsetuppassport.jsПредставлено в LocalStrategy
...
var LocalStrategy = require("passport-local").Strategy;
…
Затем выполните следующие шаги, чтобы использовать LocalStrategy для конкретной проверки:
- Запросите этого пользователя.
- Если пользователь не существует, он предложит, что аутентификация не может быть пройдена.
- Если пользователь существует, сравнивается пароль. Если совпадение будет успешным, он вернет текущего пользователя, в противном случае будет предложено «неверный пароль».
Вот как перевести эти шаги в конкретный код:
// setuppassport.js 验证代码
...
passport.use("login", new LocalStrategy(function(username, password, done) {
User.findOne({ username: username }, function(err, user) {
if(err) { return done(err); }
if (!user) {
return done(null, false, { message: "No user has that username!" });
}
user.checkPassword(password, function(err, isMatch) {
if (err) { return done(err); }
if (isMatch) {
return done(null, user);
} else {
return done(null, false, { message: "Invalid password." });
}
});
});
}));
...
Как только политика определена, ее можно вызывать в любом месте проекта.
Наконец, нам все еще нужно завершить некоторые представления и функции:
- Авторизоваться
- выход
- Редактирование личной информации после входа в систему
Во-первых, мы реализуем представление интерфейса входа в систему. существуетroutes.jsДобавьте промежуточное ПО маршрутизации входа в систему в:
...
router.get("/login", function(req, res) {
res.render("login");
});
...
в режиме входаlogin.ejs, получаем логин и пароль, после чего отправляем POST-запрос на вход:
<% include _header %>
<h1>Log in</h1>
<form action="/login" method="post">
<input name="username" type="text" class="form-control" placeholder="Username" required autofocus>
<input name="password" type="password" class="form-control" placeholder="Password" required>
<input type="submit" value="Log in" class="btn btn-primary btn-block">
</form>
<% include _footer %>
Далее нам нужно обработать запрос POST. Среди них будет использоваться функция аутентификации Passport.
// routes.js 中登陆功能代码
var passport = require("passport");
...
router.post("/login", passport.authenticate("login", {
successRedirect: "/",
failureRedirect: "/login",
failureFlash: true
}));
...
вpassport.authenticateФункция возвращает обратный вызов. Эта функция будет перенаправлять различные результаты проверки в соответствии с нашими спецификациями. Например, успешный вход перенаправляет на домашнюю страницу, а неудачный — на страницу входа.
Операция выхода из системы относительно проста, код выглядит следующим образом
// routes.js 登出部分
...
router.get("/logout", function(req, res) {
req.logout();
res.redirect("/");
});
...
Passport также добавляет информацию req.user и connect-flash. Просмотрите предыдущий код еще раз, я думаю, вы сможете лучше понять его.
...
router.use(function(req, res, next) {
// 为你的模板设置几个有用的变量
res.locals.currentUser = req.user;
res.locals.errors = req.flash("error");
res.locals.infos = req.flash("info");
next();
});
...
Войдите и выйдите из системы, чтобы сыграть в лотерею, теперь настала очередь функции редактирования личной информации.
Во-первых, давайте реализуем общую функцию промежуточного программного обеспеченияensureAuthenticated. Функция промежуточного программного обеспечения проверит разрешения текущего пользователя и, если проверка не пройдена, перенаправит его на страницу входа.
// routes.js 中的 ensureAuthenticated 中间件
...
function ensureAuthenticated(req, res, next) {
// 一个Passport提供的函数
if (req.isAuthenticated()) {
next();
} else {
req.flash("info", "You must be logged in to see this page.");
res.redirect("/login");
}
}
...
Далее мы будем вызывать эту функцию в промежуточном программном обеспечении редактирования. Потому что нам нужно убедиться, что у текущего пользователя есть права на редактирование, прежде чем мы начнем редактирование.
// GET /edit(在router.js中)
...
// 确保用户被身份认证;如果它们没有被重定向的话则运行你的请求处理
router.get("/edit", ensureAuthenticated, function(req, res) {
res.render("edit");
});
...
Далее нам нужно реализоватьedit.ejsПросмотрите файл шаблона. Содержимое этого шаблона представления очень простое, включая только изменение имени и профиля пользователя.
// views/edit.ejs
<% include _header %>
<h1>Edit your profile</h1>
<form action="/edit" method="post">
<input name="displayname" type="text" class="form-control" placeholder="Display name" value="<%= currentUser.displayName || "" %>">
<textarea name="bio" class="form-control" placeholder="Tell us about yourself!"> <%= currentUser.bio || "" %></textarea>
<input type="submit" value="Update" class="btn btn-primary btn-block">
</form>
<% include _footer %>
Наконец, нам нужно обработать измененный отправленный запрос. Перед обновлением базы здесь также требуется авторизация аутентификации.
// POST /edit(在routes.js中)
...
// 通常,这会是一个PUT请求,不过HTML表单仅仅支持GET和POST
router.post("/edit", ensureAuthenticated, function(req, res, next) {
req.user.displayName = req.body.displayname;
req.user.bio = req.body.bio;
req.user.save(function(err) {
if (err) {
next(err);
return;
}
req.flash("info", "Profile updated!");
res.redirect("/edit");
});
});
...
Код просто обновляет поля соответствующих записей в базе данных. Окончательный визуализированный вид редактирования выглядит следующим образом:
Наконец, вы можете создать некоторые тестовые данные, чтобы проверить все функциональные возможности примера приложения.
Суммировать
Эта статья содержит следующее:
- Как работает Монго.
- Использование Мангуста.
- Используйте bcrypt для шифрования определенных полей для повышения безопасности данных.
- Используйте Passport для аутентификации авторизации.
оригинальныйадрес