使用 PostgreSQL、Sequelize 和 Express.js 进行 Node.js 认证
作者:Racheal Kuranchie
来源:https://medium.com/@rachealkuranchie/node-js-authentication-with-postgresql-sequelize-and-express-js-20ae773da4c9
使用 PostgreSQL、Sequelize 和 Express.js 进行 Node.js 认证
使用 JSON Web Token(JWT)和 Cookie-Parser 进行用户认证
在本文中,我们将讨论用户注册和登录,通过使用 PostgreSQL 作为数据库、Sequelize 作为对象关系映射器(ORM),以及 Express.js 作为服务器,结合 Cookie-Parser 设置 cookie 和 JSON Web Token(JWT)。
理解概念
用户注册是应用程序中的一个相关步骤,因此涉及到认证和授权的概念。认证和授权是出于安全目的的要求。
这两个概念经常交替使用,这使得难以区分你想要实现的功能以及它所需的内容。
在我们深入认证之前,让我们澄清这两个概念之间的区别。认证是确定用户是否是他们声称的那个人。通过这一点,它检查数据库以查看用户的凭据是否与数据库中授权用户的凭据匹配。相比之下,授权指定数据用户可以访问你的应用程序。
Cookies 是在请求中发送到 Web 浏览器的小数据片段。它们作为键值对存储在 Web 浏览器中。键作为给特定 cookie 命名的名称。值表示所需的特定数据。Cookies 在大多数应用程序中都是必不可少的。例如,cookies 可以保持用户登录到你的应用程序。对于这个应用程序,我们将使用 Cookie-Parser 和 JSON Web Token 生成一个令牌,用作 cookie 的值。
示例 cookie — admin: ydfgyfgeuygfegyfgirgcfygfyhgcyg
JSON Web Token(JWT)是一个开放标准(RFC 7519),它定义了一种紧凑且自包含的方式来安全地传输信息并在各方之间作为 JSON 对象传递。JWT 由三个由点分隔的部分构成,即头部、负载和签名。负载特性允许我们使用用户 ID 和密钥生成令牌,这使得它更适合用于为特定用户设置 cookie。
目录
- 第 1 步:创建 Node Express 项目
- 第 2 步:数据库和服务器配置
- 第 3 步:设置用户模式
- 第 4 步:中间件设置
- 第 5 步:设置环境变量
- 第 6 步:设置控制器
- 第 7 步:配置 Express 路由器
- 第 8 步:重构服务器
- 第 9 步:测试
先决条件
首先,你必须安装以下软件:
第 1 步:创建 Node Express 项目
通过以下命令创建项目文件夹:
mkdir backend
cd backend
现在,使用以下命令创建 package.json 文件:
npm init -y
NB: 参考 Sequelize 文档 => 文档链接
接下来,安装这些包:
- Express — Node.js 框架
npm install express
- Sequelize — PostgreSQL 的 ORM
npm install --save sequelize
- Pg 和 pg-hstore — pg 是 Node.js 的 PostgreSQL 客户端,pg-hstore 是一个用于将 JSON 数据序列化为 hstore 格式的 node 包 =>
npm install --save pg pg-hstore
- Nodemon — 当文件更改时自动重启 Node 应用程序 =>
npm install nodemon –save-dev
- Bcrypt — 用于密码哈希 =>
npm install bcrypt
- Dotenv => 用于访问你的环境变量 =>
npm install dotenv
- JSON Web Token => 用于生成令牌 =>
npm install jsonwebtoken
- CookieParser => 用于设置 cookie =>
npm install cookie-parser
安装完成后,在 server.js 文件中添加以下内容,并修改你的 package.json 文件,添加一个启动脚本,如下所示:
// package.json
{
"name": "backend",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"start": "nodemon server.js",
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"bcrypt": "^5.0.1",
"cookie-parser": "^1.4.6",
"dotenv": "^16.0.1",
"express": "^4.18.1",
"jsonwebtoken": "^8.5.1",
"pg": "^8.7.3",
"pg-hstore": "^2.3.4",
"sequelize": "^6.19.0"
},
"devDependencies": {
"nodemon": "^2.0.16"
}
}
第 2 步:数据库和服务器配置
在 server.js 文件中,导入你的模块,设置你的端口,并监听服务器连接。
// Server.js
// 导入模块
const express = require('express')
const sequelize = require('sequelize')
const dotenv = require('dotenv').config()
const cookieParser = require('cookie-parser')
// 设置你的端口
const PORT = process.env.PORT || 8080
// 将变量 app 分配给 express
const app = express()
// 中间件
app.use(express.json())
app.use(express.urlencoded({ extended: true }))
app.use(cookieParser())
// 监听服务器连接
app.listen(PORT, () => console.log(`Server is connected on ${PORT}`))
接下来是使用 Model Views Controller (MVC) 方法创建你的文件夹结构:
- 控制器
- 模型
- 路由
- 中间件
- 数据库配置
打开你的 pgAdmin 并创建你的数据库。
在 Models 文件夹中创建一个 index.js 文件,并使用 Sequelize ORM 设置你的数据库。
// Example for postgres database connection
const sequelize = new Sequelize('postgres://user:pass@example.com:5432/dbname')
// Model/index.js
// 导入模块
const { Sequelize, DataTypes } = require('sequelize')
// 使用 postgres 方言指定数据库连接
// 我的数据库端口是 5433
// 数据库名称是 discover
const sequelize = new Sequelize('postgres://postgres:123456@localhost:5433/discover', { dialect: "postgres" })
// 检查连接是否完成
sequelize.authenticate().then(() => {
console.log('Database connected to discover')
}).catch((err) => {
console.log(err)
})
// 连接到模型
db.users = require('./userModel')(sequelize, DataTypes)
// 导出模块
module.exports = db
第 3 步:设置你的用户模式
一旦你的数据库设置好了,在 Models 文件夹中创建另一个文件 userModel.js,使用 Sequelize 设置你的用户模式。
// user model
module.exports = (sequelize, DataTypes) => {
const User = sequelize.define("user", {
userName: {
type: DataTypes.STRING,
allowNull: false,
},
email: {
type: DataTypes.STRING,
unique: true,
isEmail: true, // 检查电子邮件格式
allowNull: false,
},
password: {
type: DataTypes.STRING,
allowNull: false,
},
}, { timestamps: true })
return User
}
第 4 步:创建你的中间件
下一步是创建一个函数,该函数在将用户保存到数据库之前检查数据库中现有的用户名和电子邮件,以避免重复。在 middleware 文件夹中创建一个 userAuth.js 文件。
// 导入模块
const express = require("express");
const db = require("../Models");
// 将 db.users 分配给 User 变量
const User = db.users;
// 函数用于检查数据库中是否已存在用户名或电子邮件
// 这是为了避免拥有相同用户名和电子邮件的两个用户
const saveUser = async (req, res, next) => {
// 搜索数据库以查看用户是否存在
try {
const username = await User.findOne({
where: {
userName: req.body.userName,
},
});
// 如果用户名在数据库中存在,则以 409 状态响应
if (username) {
return res.json(409).send("username already taken");
}
// 检查电子邮件是否已存在
const emailcheck = await User.findOne({
where: {
email: req.body.email,
},
});
// 如果电子邮件在数据库中存在,则以 409 状态响应
if (emailcheck) {
return res.json(409).send("Authentication failed");
}
next();
} catch (error) {
console.log(error);
}
};
// 导出模块
module.exports = {
saveUser,
};
第 5 步:设置你的环境变量
.env 文件用于存储密钥。在你的根文件夹中创建一个 .env 文件,并在变量中保存随机字母。
例如:secretKey = ydwygyegyegcveyvcyegc
第 6 步:设置控制器
控制器包含将用户注册到我们的数据库和登录的逻辑。创建一个 userController.js 文件。控制器中有两个主要函数:
- signup:在将密码保存到数据库之前,使用 bcrypt 对密码进行哈希
- login:
- 在数据库中查找请求的电子邮件,如果存在
- 如果密码与数据库中的现有密码匹配,
- 使用 (JWT) 生成带有用户 ID 的令牌,并使用 Cookie-Parser 为用户设置 cookie
- 返回用户信息
// 导入模块
const bcrypt = require("bcrypt");
const db = require("../Models");
const jwt = require("jsonwebtoken");
// 将 users 分配给变量 User
const User = db.users;
// 用户注册
// 使用 bcrypt 在保存到数据库之前对用户密码进行哈希
const signup = async (req, res) => {
try {
const { userName, email, password } = req.body;
const data = {
userName,
email,
password: await bcrypt.hash(password, 10),
};
// 保存用户
const user = await User.create(data);
// 如果捕获到用户详细信息
// 使用用户 ID 和 env 文件中的秘密密钥生成令牌
// 设置带有生成令牌的 cookie
if (user) {
let token = jwt.sign({ id: user.id }, process.env.secretKey, {
expiresIn: 1 * 24 * 60 * 60 * 1000,
});
} else {
return res.status(409).send("Details are not correct");
}
} catch (error) {
console.log(error);
}
};
// 登录认证
const login = async (req, res) => {
try {
const { email, password } = req.body;
// 通过电子邮件查找用户
const user = await User.findOne({
where: {
email: email,
},
});
// 如果找到用户电子邮件,使用 bcrypt 比较密码
if (user) {
const isSame = await bcrypt.compare(password, user.password);
} else {
return res.status(401).send("Authentication failed");
}
} catch (error) {
console.log(error);
}
};
module.exports = {
signup,
login,
};
第 7 步:配置 Express 路由器
在 routes 文件夹中,通过使用 Express.js 提供的 Router 模块设置应用程序的路由。因此,在 routes 文件夹中创建一个名为 userRoutes.js 的文件。在 signup 路由中,传递中间件以检查数据库中用户名和电子邮件的重复项。
// 导入模块
const express = require("express");
const userController = require("../Controllers/userController");
const { signup, login } = userController;
const userAuth = require("../Middlewares/userAuth");
const router = express.Router();
// signup 端点
// 将中间件函数传递给 signup
router.post("/signup", userAuth.saveUser, signup);
// login 路由
router.post("/login", login);
module.exports = router;
第 8 步:重构服务器代码
在导入路由和数据库模块后,你的最终 server.js 应该看起来像这样:
// 导入模块
const express = require("express");
const sequelize = require("sequelize");
const dotenv = require("dotenv").config();
const cookieParser = require("cookie-parser");
const db = require("./Models");
const userRoutes = require("./Routes/userRoutes");
// 设置你的端口
const PORT = process.env.PORT || 8080;
// 将变量 app 分配给 express
const app = express();
// 中间件
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
// 同步数据库并强制设置为 false,以防我们丢失数据
db.sequelize.sync({ force: true }).then(() => {
console.log("db has been re sync");
});
// 用户 API 的路由
app.use("/api/users", userRoutes);
// 监听服务器连接
app.listen(PORT, () => console.log(`Server is connected on ${PORT}`));
第 9 步:测试
使用 Postman 或 REST 客户端测试你的端点。
打开终端并运行,npm start
注册 API
curl -X POST --data '{
"userName": "aichaoxy",
"email": "aichaoxy@example.com",
"password": "password"
}' http://localhost:8080/api/users/signup
登录 API
curl -X POST --data '{
"email": "aichaoxy@example.com",
"password": "password"
}' http://localhost:8080/api/users/login
结论
祝贺!你已成功使用 Sequelize(ORM)、PostgreSQL 和 Express 完成了用户认证。