Sequelize 是一个基于 Promise 的 node.js ORM。它可以与 postgresql、mysql、MariaDB、sqlite 和 MSSQL 一起使用。在本教程中,我们将为 Web 应用程序的用户实施身份验证。我们将使用流行的 Node 身份验证中间件 passport 与Sequelize和 MySQL 一起实现用户注册和登录。
入门
确保您的机器上安装了以下内容:
节点
MySQL
在本教程中,我们将使用 Node.js 和 Express 框架,因此我们继续并开始安装我们需要的东西。
1. 生成一个package.json文件
为您的应用创建一个目录。在此目录中,从终端或命令提示符运行它:
1 | npm init |
这会初始化 npm 依赖管理器。这将显示一系列提示,我们将快速完成这些提示。
键入您的应用程序的名称,不带空格的name。
按Enter使用默认版本。
输入描述或将其留空。
对于entry point,输入server.js。
您可以按Enter接受其余提示的默认值。
2.安装依赖
本教程的主要依赖项是:
表达
续集
MySQL
护照
护照本地策略
特快专场
Bcryptjs
用于视图的 Express handlebars
要安装它们,请从终端或命令提示符处依次运行以下命令。
npm install express --save npm install sequelize --save npm install sequelize --save npm install mysql --save npm install passport --save npm install passport-local --save npm install express-session --save npm install bcryptjs --save npm install express-handlebars --save npm install mysql2 --save
如果您为此项目使用 Git,请创建一个.gitignore文件并将以下行添加到其中:
1 | node_modules |
3.设置应用程序
在根项目目录中,我们将设置应用程序并创建一个名为server.js的新文件。这将是您启动应用程序时的主文件。
在server.js文件中,添加以下代码:
var express = require('express'); var app = express(); app.get('/', function (req, res) { res.send('Welcome to Passport with Sequelize'); }); app.listen(5000, function (err) { if (!err) console.log("Site is live"); else console.log(err) });
第一行将 express 模块分配给一个变量express。然后我们初始化 express 并将其分配给变量app。
然后我们让应用程序监听端口 5000。您可以选择计算机上的任何空闲端口号。
接下来,当向/发出 GET 请求时,我们调用app.get()express 路由函数以响应“ Welcome to Passport with Sequelize ” 。
我们可以使用以下命令在我们的计算机上运行我们刚刚创建的服务器:
1 | node server.js |
如果您 在访问http://localhost:5000/时看到文本Welcome to Passport with Sequelize,那么恭喜!否则,请检查您是否按照上面的说明完成了所有操作。
接下来,我们导入一些我们需要的模块,比如护照和快速会话。
在代码片段之后var app = express(),我们添加以下行:
var passport = require('passport'); var session = require('express-session'); |
在这两行中,我们导入passport模块和express-session模块并将它们分配给变量。需要和模块passport来express-session处理身份验证。
我们需要一个模块来提取传入请求的整个主体并以更易于使用的格式公开它。在这种情况下,我们将使用 JSON 格式。在最近的 express 版本之前,您需要使用 body-parser 模块,但在 express 版本 4.16+ 中,express 具有内置的中间件功能,可以解析带有 JSON 有效负载的传入请求,并且基于body-parser。
为了让我们的应用程序使用 express 内置的正文解析器,我们将这些行添加到导入行下方,在它们之间留出一些空间:
app.use(express.urlencoded({ extended: true }) ); app.use(express.json());
接下来,我们初始化护照以及快速会话和护照会话,并将它们都添加为中间件。我们通过在上面的导入行之后添加这些行来做到这一点,再次留出一些空间。
// For Passport app.use(session({ secret: 'keyboard cat', resave: true, saveUninitialized:true })); // session secret app.use(passport.initialize()); app.use(passport.session()); // persistent login sessions
4.创建身份验证流程
我们现在将开始进行实际的身份验证。我们将分四步进行:
使用 MySQL 设置 Sequelize。
创建用户模型。
设置视图。
编写护照策略。
第 1 步:使用 MySQL 设置 Sequelize
首先,我们在 MySQL 中创建一个数据库。给它你喜欢的名字。为了本教程的目的,让我们创建一个以sequelize_passportMySQL 命名的数据库。然后我们设置配置来处理数据库细节。
首先,让我们导入 dot-env 模块来处理环境变量。在你的根项目文件夹中运行它:
1 | npm install --save dotenv |
然后我们将它导入到主服务器文件server.js中,就在其他导入的下方。
1 | var env = require('dotenv').config(); |
接下来,我们在项目根文件夹中创建一个文件并将其命名为.env,如果我们使用 Git,请将其添加到.gitignore文件中。
在此之后,我们通过添加以下行将我们的环境添加到.env文件中:
NODE_ENV='development'
然后我们创建一个config.json文件,Sequelize 将使用它来管理不同的环境。
首先要做的是在项目根目录中创建一个名为app的文件夹,其中包含一个名为config的文件夹。在 config 文件夹中,我们创建一个config.json文件。如果您要推送到存储库,则应忽略此文件。为此,请将以下代码添加到您的.gitignore:
1 | app/config/config.json |
然后,我们将以下代码粘贴到我们的config.json文件中。
{ "development": { "username": "root", "password": yourPasswordHere, "database": "sequelize_passport", "host": "127.0.0.1", "dialect": "mysql" }, "test": { "username": "", "password": null, "database": "", "host": "", "dialect": "mysql" }, "production": { "username": "", "password": null, "database": "", "host": "127.0.0.1", "dialect": "mysql" } }
请记住将上面开发块中的值替换为您的数据库身份验证详细信息。
现在是时候创建模型文件夹了。
在app 文件夹中,我们创建一个名为models的新文件夹,并在models文件夹中创建一个名为index.js的新文件。
在index.js文件中,我们粘贴下面的代码片段。
"use strict"; var fs = require("fs"); var path = require("path"); var Sequelize = require("sequelize"); var env = process.env.NODE_ENV || "development"; var config = require(path.join(__dirname, '..', 'config', 'config.json'))[env]; var sequelize = new Sequelize(config.database, config.username, config.password, config); var db = {}; fs .readdirSync(__dirname) .filter(function (file) { return (file.indexOf(".") !== 0) && (file !== "index.js"); }) .forEach(function (file) { var model = sequelize.import(path.join(__dirname, file)); db[model.name] = model; }); Object.keys(db).forEach(function (modelName) { if ("associate" in db[modelName]) { db[modelName].associate(db); } }); db.sequelize = sequelize; db.Sequelize = Sequelize; module.exports = db; 该文件用于导入我们放置在models文件夹中的所有模型并导出它们。为了测试一切正常,我们将其添加到server.js文件中。 //Models var models = require("./app/models"); //Sync Database models.sequelize.sync().then(function() { console.log('Nice! Database looks fine'); }).catch(function(err) { console.log(err, "Something went wrong with the Database Update!"); });
在这里,我们正在导入模型,然后调用models.sequelize同步函数。运行它以查看是否一切正常:
1 | node server.js |
如果您收到消息“站点已上线。很好!数据库看起来不错”,那么您已成功设置 Sequelize。
如果没有,请仔细检查上述步骤并尝试在帮助下调试问题。
第 2 步:创建用户模型
接下来我们要做的是创建用户模型,它基本上是用户表。这将包含基本的用户信息。
在我们的模型文件夹中,我们创建一个文件并将其命名为user.js。该文件的完整路径应该是app/models/user.js 。
打开user.js文件并添加以下代码:
module.exports = function(sequelize, Sequelize) { var User = sequelize.define('user', { id: { autoIncrement: true, primaryKey: true, type: Sequelize.INTEGER }, firstname: { type: Sequelize.STRING, notEmpty: true }, lastname: { type: Sequelize.STRING, notEmpty: true }, username: { type: Sequelize.TEXT }, about: { type: Sequelize.TEXT }, email: { type: Sequelize.STRING, validate: { isEmail: true } }, password: { type: Sequelize.STRING, allowNull: false }, last_login: { type: Sequelize.DATE }, status: { type: Sequelize.ENUM('active', 'inactive'), defaultValue: 'active' } }); return User; }
现在运行:
1 | node server.js |
您应该会看到熟悉的“站点已上线。不错!数据库看起来不错”的消息。这意味着我们的 Sequelize 模型已成功同步,如果您检查数据库,您应该会看到一个包含指定列的 users 表。
第 3 步:设置视图
在本节中,我们将为客户端设置视图。首先,让我们创建注册视图并将其连接起来。
首先要做的是导入express-handlebars将在本教程中用于视图的模块。Express HandleBars 可用于将服务器端的数据作为网页呈现给客户端。
将此代码段添加到server.js 文件中。
var exphbs = require('express-handlebars')
此时您的导入块应如下所示。
var express = require('express'); var app = express(); var passport = require('passport'); var session = require('express-session'); var env = require('dotenv').config(); var exphbs = require('express-handlebars');
为了在 express 中使用把手,我们将扩展名为.hbs的 html 代码保存在views文件夹中,我们将很快在根目录中创建该文件夹,因为把手在views文件夹中查找页面。
接下来,我们在server.js文件中添加以下行。
//For Handlebars app.set('views', './app/views'); app.engine('hbs', exphbs.engine({ extname: '.hbs', defaultLayout: false, layoutsDir: "views/layouts/" })); app.set('view engine', '.hbs');
现在,在我们的 app 文件夹中,我们创建了三个名为views 、controllers和routes的文件夹。
在views文件夹中,我们创建一个名为signup.hbs的文件并粘贴到下面的代码片段中。
<!DOCTYPE html> <html> <head> <title>Sign up layout</title> <link rel="stylesheet" href="/styles.css"> </head> <body> <h2>Passport With Sequelize and MySQL</h2> <form id="signup" name="signup" method="post" action="/signup"> <label for="email">Email Address: </label> <input class="text" name="email" type="email" /> <label for="firstname">First Name: </label> <input name="firstname" type="text" /> <label for="lastname">Last Name: </label> <input name="lastname" type="text" /> <label for="password">Password: </label> <input name="password" type="password" /> <input class="btn" type="submit" value="Sign Up" /> </form> </body> </html>
然后,在我们的控制器文件夹中,我们创建一个名为authController.js的新文件。
在这个文件中,我们为注册路由粘贴以下控制器,稍后我们将创建它。
var exports = module.exports = {}; exports.signup = function(req, res) { res.render('signup'); };
接下来,我们创建一个注册路由。在 routes 文件夹中,我们创建一个名为auth.js的新文件,然后在此文件中,我们导入该authController文件并定义注册路由。
var authController = require('../controllers/authController.js'); module.exports = function(app) { app.get('/signup', authController.signup); };
现在,我们将在server.js中导入此路由并将应用程序作为参数传递。在server.js中,模型导入后,添加以下行:
//Routes var authRoute = require('./app/routes/auth.js')(app);
运行这个:
1 | node server.js |
现在,访问http://localhost:5000/signup,您将看到注册表单。
让我们重复登录表单的步骤。和以前一样,我们将在我们的视图文件夹中创建一个名为signin.hbs的文件,并将以下 HTML 代码粘贴到其中:
<!DOCTYPE html> <html> <head> <title>Sign in Layout</title> <link rel="stylesheet" href="/styles.css"> </head> <body> <h2>Passport With Sequelize and MySQL</h2> <form id="signin" name="signin" method="post" action="signin"> <label for="email">Email Address</label> <input class="text" name="email" type="text" /> <label for="password">Password</label> <input name="password" type="password" /> <input class="btn" type="submit" value="Sign In" /> </form> </body> </html>
然后,在app/controllers/authcontroller.js中添加一个用于登录的控制器。
exports.signin = function(req, res) { res.render('signin'); };
然后,在app/routes/auth.js中,我们添加一个用于登录的路由,如下所示:
app.get('/signin', authController.signin);
现在,当您运行时:
1 | node server.js |
并访问http://localhost:5000/signin/,您应该会看到登录表单。
最后也是主要的一步是编写我们的护照策略。
第 4 步:编写护照策略
在app/config中,我们创建一个名为passport的新文件夹。
然后,在我们的新文件夹app/config/passport中,我们创建一个新文件并将其命名为passport.js。该文件将包含我们的护照策略。
在passport.js中,我们将使用用户模型和护照。
首先,我们 import bcryptjs,我们需要它来保护密码。
var bCrypt = require('bcryptjs');
然后,我们添加一个module.exports这样的块:
module.exports = function(passport, user) {} |
在这个块中,我们初始化passport-local策略和用户模型,它们将作为参数传递。下面是我们如何做到这一点:
module.exports = function(passport, user) { var User = user; var LocalStrategy = require('passport-local').Strategy;} |
然后我们用这样的实例定义我们的自定义策略LocalStrategy:
passport.use('local-signup', new LocalStrategy( { usernameField: 'email', passwordField: 'password', passReqTocallback: true // allows us to pass back the entire request to the callback }, ))
现在我们已经声明了我们的usernameField和passwordField(护照变量)是什么请求字段。
最后一个变量passReqToCallback允许我们将整个请求传递给回调,这对于注册特别有用。
在最后一个逗号之后,我们添加了这个回调函数。
function(req, email, password, done) {} |
在这个函数中,我们将处理存储用户的详细信息。
首先,我们在回调函数中添加哈希密码生成函数。
var generateHash = function(password) { return bCrypt.hashSync(password, bCrypt.genSaltSync(8), null);}; |
然后,使用我们之前初始化的 Sequelize 用户模型User,我们检查用户是否已经存在,如果不存在,我们添加它们。
User.findOne({ where: { email: email } }).then(function(user) { if (user) { return done(null, false, { message: 'That email is already taken' }); } else { var userPassword = generateHash(password); var data = { email: email, password: userPassword, firstname: req.body.firstname, lastname: req.body.lastname }; User.create(data).then(function(newUser, created) { if (!newUser) { return done(null, false); } if (newUser) { return done(null, newUser); } }); } });
User.create()是一种用于向数据库添加新条目的 Sequelize 方法。请注意,数据对象中的值是从req.body包含我们注册表单的输入的对象中获取的。
你passport.js应该是这样的:
//load bcrypt var bCrypt = require('bcryptjs'); module.exports = function(passport, user) { var User = user; var LocalStrategy = require('passport-local').Strategy; passport.use('local-signup', new LocalStrategy( { usernameField: 'email', passwordField: 'password', passReqToCallback: true // allows us to pass back the entire request to the callback }, function(req, email, password, done) { var generateHash = function(password) { return bCrypt.hashSync(password, bCrypt.genSaltSync(8), null); }; User.findOne({ where: { email: email } }).then(function(user) { if (user) { return done(null, false, { message: 'That email is already taken' }); } else { var userPassword = generateHash(password); var data = { email: email, password: userPassword, firstname: req.body.firstname, lastname: req.body.lastname }; User.create(data).then(function(newUser, created) { if (!newUser) { return done(null, false); } if (newUser) { return done(null, newUser); } }); } }); } )); }
现在我们将在server.js中导入策略。为此,我们在routesimport下方添加这些行。
//load passport strategies require('./app/config/passport/passport.js')(passport, models.user); |
您的server.js现在应该如下所示:
var express = require('express'); var app = express(); var passport = require('passport'); var session = require('express-session'); var env = require('dotenv').config(); var exphbs = require('express-handlebars'); app.use(express.urlencoded({ extended: true }) ); app.use(express.json()); // For Passport app.use(session({ secret: 'keyboard cat', resave: true, saveUninitialized: true })); // session secret app.use(passport.initialize()); app.use(passport.session()); // persistent login sessions //For Handlebars app.set('views', './app/views'); app.engine('hbs', exphbs.engine({ extname: '.hbs', defaultLayout: false, layoutsDir: "views/layouts/" })); app.set('view engine', '.hbs'); app.get('/', function(req, res) { res.send('Welcome to Passport with Sequelize'); }); //Models var models = require("./app/models"); //Routes var authRoute = require('./app/routes/auth.js')(app); //load passport strategies require('./app/config/passport/passport.js')(passport, models.user); //Sync Database models.sequelize.sync().then(function() { console.log('Nice! Database looks fine'); }).catch(function(err) { console.log(err, "Something went wrong with the Database Update!"); }); app.listen(5000, function(err) { if (!err) console.log("Site is live"); else console.log(err); });
现在我们将把策略实际应用到我们的/signup路由中。以下是我们的做法:
首先,我们去app/routes/auth.js并添加一个用于发布到注册的路由,就像这样。
app.post('/signup', passport.authenticate('local-signup', { successRedirect: '/dashboard', failureRedirect: '/signup' } ));
由于我们需要护照,我们需要将它传递给这个方法。我们可以在这个脚本中导入护照或从 server.js 传递它。让我们做后者。
修改此文件app/routes/auth.js中导出的函数以将护照作为参数。修改后app/routes/auth.js中的代码应如下所示。
var authController = require('../controllers/authcontroller.js'); module.exports = function(app, passport) { app.get('/signup', authController.signup); app.get('/signin', authController.signin); app.post('/signup', passport.authenticate('local-signup', { successRedirect: '/dashboard', failureRedirect: '/signup' } )); };
然后,在server.js中,我们修改routes导入并添加护照作为参数,如下所示:
var authRoute = require('./app/routes/auth.js')(app,passport);
现在,转到注册 URL http://localhost:5000/signup/并尝试注册。
当您尝试注册时,您将收到错误“无法将用户序列化到会话中”。这是因为护照必须在会话中保存一个用户 ID,并在需要时使用它来管理检索用户详细信息。
为了解决这个问题,我们将在app/config/passport/passport.js文件中实现护照的序列化和反序列化功能。
首先,我们添加序列化函数。在这个函数中,我们会将用户 ID保存到会话中。为此,我们在本地策略的初始化下方添加以下行。
//serialize passport.serializeUser(function(user, done) { done(null, user.id); });
接下来,我们实现反序列化功能。在序列化函数下方添加该函数。
// deserialize user passport.deserializeUser(function (id, done) { User.findByPk(id).then(function (user) { if (user) { done(null, user.get()); } else { done(user.errors, null); } }); });
在上面的反序列化函数中,我们使用 Sequelize findByPkpromise 来获取用户,如果成功,则返回一个 Sequelize 模型的实例。要从此实例中获取 User 对象,我们使用 Sequelize getter 函数,如下所示user.get():
现在再次运行:
1 | node server.js |
并尝试注册。如果您收到“ Cannot GET /dashboard ”消息,万岁!这意味着我们的身份验证成功。请记住,我们在 routes/auth.js 中的 passport.authenticate 方法中重定向到/dashboard 。
现在让我们继续添加这条路线。然后,添加一些中间件以确保只有在用户登录会话时才能访问该页面。
在我们的 app/views 文件夹中,我们创建一个名为dashboard.hbs的新文件,并在其中添加以下 HTML 代码。
<!DOCTYPE html> <html> <head> <title>Passport with Sequelize</title> <link rel="stylesheet" href="/styles.css"> </head> <body> <h2>Dashboard</h2> <h5>Hurray! you are logged in ?.</h5> </body> </html>
在 routes/auth.js 中,我们在module.exports块中添加这一行:
1 | app.get('/dashboard',authController.dashboard); |
接下来,我们转到app/controllers/authController.js并添加仪表板控制器。
exports.dashboard = function(req, res) { res.render('dashboard'); }; 您的AuthController.js应该如下所示: var exports = module.exports = {} exports.signup = function(req, res) { res.render('signup'); } exports.signin = function(req, res) { res.render('signin'); } exports.dashboard = function(req, res) { res.render('dashboard'); }
现在,再次运行该应用程序,并尝试使用与您之前使用的电子邮件地址不同的电子邮件地址进行注册。您将被适当地重定向到/dashboard 路由。
但是/dashboard不是受保护的路由,这意味着即使用户没有登录,他们也可以看到它。我们不希望这样,所以我们将添加一个/logout路由来注销用户,然后保护该路由并测试我们所做的事情。
在routes/auth.js我们添加这一行:
1 | app.get('/logout',authController.logout); |
然后我们在app/controllers/authController.js中添加控制器。
exports.logout = function(req, res) { req.session.destroy(function(err) { res.redirect('/'); }); }
现在再次运行应用程序并使用不同的电子邮件地址注册。
之后,访问http://localhost:5000/logout以注销用户。现在访问http://localhost:5000/dashboard。
您会注意到它非常易于访问。让我们添加一些自定义中间件来保护该路由。
为此,我们打开app/routes/auth.js 并将 这个函数添加到module.exports块中,在所有其他代码行的下方。
function isLoggedIn(req, res, next) { if (req.isAuthenticated()) return next(); res.redirect('/signin'); }
然后我们将仪表板路由处理程序修改为如下所示:
1 | app.get('/dashboard',isLoggedIn, authController.dashboard); |
现在,当您再次运行应用程序并尝试访问仪表板页面并且您没有登录时,您应该被重定向到登录页面。
哇!现在是实施最后一部分的时候了:登录。
首先,我们将在app/config/passport/passport.js中添加一个新的本地登录策略。
//LOCAL SIGNIN passport.use('local-signin', new LocalStrategy( { // by default, local strategy uses username and password, we will override with email usernameField: 'email', passwordField: 'password', passReqToCallback: true // allows us to pass back the entire request to the callback }, function(req, email, password, done) { var User = user; var isValidPassword = function(userpass, password) { return bCrypt.compareSync(password, userpass); } User.findOne({ where: { email: email } }).then(function(user) { if (!user) { return done(null, false, { message: 'Email does not exist' }); } if (!isValidPassword(user.password, password)) { return done(null, false, { message: 'Incorrect password.' }); } var userinfo = user.get(); return done(null, userinfo); }).catch(function(err) { console.log("Error:", err); return done(null, false, { message: 'Something went wrong with your Signin' }); }); } ));
在此策略中,该函数将输入的密码与 bCrypt 比较方法进行比较,因为我们将密码存储在 bcrypt中。如果详细信息正确,我们的用户将登录。 isValidPassword
现在,转到routes/auth.js并将发布的路由添加到/ signin 。
app.post('/signin', passport.authenticate('local-signin', { successRedirect: '/dashboard', failureRedirect: '/signin' } )); 完成后,您的routes/auth.js应该如下所示。 var authController = require('../controllers/authcontroller.js'); module.exports = function(app, passport) { app.get('/signup', authController.signup); app.get('/signin', authController.signin); app.post('/signup', passport.authenticate('local-signup', { successRedirect: '/dashboard', failureRedirect: '/signup' } )); app.get('/dashboard', isLoggedIn, authController.dashboard); app.get('/logout', authController.logout); app.post('/signin', passport.authenticate('local-signin', { successRedirect: '/dashboard', failureRedirect: '/signin' } )); function isLoggedIn(req, res, next) { if (req.isAuthenticated()) return next(); res.redirect('/signin'); } }
现在运行应用程序并尝试登录。您应该能够使用您在注册时使用的任何详细信息登录,并且您将被定向到 http://localhost:5000/dashboard/ 。
恭喜您完成本教程!我们已经成功地将 Sequelize 和 Passport 与 MySQL 数据库一起使用。
为了使我们的应用程序更具吸引力,我们将添加一些 CSS 样式。样式代码和本教程的完整代码可以在GitHub上找到。
结论
我们关于使用 Passport 通过 Sequelize 和 MySQL 对用户进行身份验证的教程到此结束。Sequelize 是一个非常有用的 ORM,用于在使用 Node.js 时处理 MySQL。我个人发现它非常有用,您绝对应该考虑在您的下一个 Node-MySQL 应用程序中使用它。
- 第 1 步:使用 MySQL 设置 Sequelize
- 第 2 步:创建用户模型
- 第 3 步:设置视图
- 第 4 步:编写护照策略
发表评论