在开始本教程之前,我们首先要了解什么是Prisma,以及为什么它是刚接触Express的开发者的一个好选择。我们还将讨论PostgreSQL,以及如何将其用于数据库模式和结构化。
我们还将了解Prisma的有效性,以及如何使用它进行基本认证,同时还有代码样本和测试实例来帮助你跟上这个教程。
什么是Prisma?
Prisma是一个开源的ORM,它允许你轻松地管理你的数据库并与之互动。这是通过Prisma模式完成的,在这个地方你可以使用Prisma模式语言定义你的数据库模型和关系。
你可以从头开始运行你的模式,或者通过内省现有的数据库来生成它。然后,你可以使用Prisma Client与你的数据库进行交互,并使用Prisma Migrate将你的模式迁移到数据库中。
Prisma支持PostgreSQL、MySQL、SQLite和Microsoft SQL Server。Prisma与每个Node.js后端框架互动,使数据库管理和迁移变得简单。
在Express中用Prisma建立一个认证方案
首先,我们将设置一个Express应用程序并添加Prisma。然后,我们将使用第三方包,如JWT,用于基于令牌的认证,以创建一个认证方案。最后,我们将介绍如何运行测试并确保我们的认证方案正确运行。
前提条件
要学习本教程,你应该对这些技术有一定的了解,并在你的电脑上安装它们的最新版本。
设置Express
为了安装Express,我们必须首先使用npm初始化我们的应用程序。要做到这一点,请在你的终端运行以下代码。
mkdir express-prisma
cd express-prisma
npm init -y
然后,我们可以使用npm在我们新创建的应用程序中使用以下代码安装Express。
npm install express
接下来我们使用Docker设置我们的PostgreSQL。
要做到这一点,我们将使用以下composer命令创建一个新的Docker文件。
nano docker-compose.yml
然后在我们的docker-compose.yml 文件中,我们可以添加以下代码来连接到数据库。
version: '3.8'
services:
postgres:
image: postgres:10.3
restart: always
environment:
- POSTGRES_USER=sammy
- POSTGRES_PASSWORD=your_password
volumes:
- postgres:/var/lib/postgresql/data
ports:
- '5432:5432'
volumes:
postgres:
注意,POSTGRES_USER 和POST_PASSWORD 是预设的用户名和密码,将用于访问数据库。
安装和配置Prisma
当我们的Express应用程序的安装和配置完成后,我们现在可以继续使用npm将Prisma安装到我们的应用程序中。要做到这一点,只需使用下面的命令。
npx prisma init
这将创建一个新的Prisma文件夹,该文件夹将包含schema.prisma ,如果不存在,也将创建一个.env 文件。
文件生成后,打开.env 文件,并添加一个链接到你的数据库。
DATABASE_URL="postgresql://<NAME_OF_DATABASE>:<DATABASE_PASSWORD>@localhost:5432/express-prisma?schema=public"
请确保使用你自定义的数据库名称和密码。
现在我们已经完成了Prisma的配置,我们可以创建一个Prisma模式并添加我们的认证方案。
创建一个Prisma模式
我们将首先创建一个模式,它将包含将被迁移到数据库的用户参数。这些将使我们能够与他们互动,以完成认证。
要添加一个模式,请进入prisma/schema.prisma 文件并添加以下代码。
model User {
id Int @id @default(autoincrement())
email String @unique
name String?
password String?
}
一旦完成,我们就可以运行我们的迁移,它将在数据库中创建表user ,并为其添加列。
要运行迁移,在你的终端添加下面的代码并运行它。
npx prisma migrate dev --name "init" --preview-feature
如果迁移成功,将在之前创建的prisma 文件夹中创建一个新的文件夹。这个文件夹将被称为migrations ,并将包含一个SQL文件。
我的文件是20210613163752_init/migration.sql ,文件migration.sql ,其中包含在模式中创建的SQL结构。
-- CreateTable
CREATE TABLE "User" (
"id" SERIAL NOT NULL,
"email" TEXT NOT NULL,
"name" TEXT,
"password" TEXT,
PRIMARY KEY ("id")
);
-- CreateIndex
CREATE UNIQUE INDEX "User.email_unique" ON "User"("email");
使用Prisma客户端
Prisma客户端是一个自动生成的、类型安全的查询生成器,你可以用它来从Node.js或TypeScript应用程序中以编程方式读写数据库中的数据。你将在你的REST API路线中使用它来访问数据库,取代传统的ORM、普通SQL查询、自定义数据访问层或任何其他与数据库对话的方法。
要在你的项目中安装Prisma客户端,只需在终端输入以下命令并运行它。
npm install @prisma/client
这将使你能够在你项目的任何地方使用Prisma Client,反过来,允许你与你的数据库进行交互。
索引要求
在Prisma Client设置好后,我们可以继续添加我们的控制器,它将与我们的路由(指向我们控制器中的指定功能)进行交互。我们还将添加我们的服务,这些服务与数据库或Prisma互动。
首先,我们将创建几个文件和文件夹来存放它们。首先是路由 - 我们将创建一个名为routes 的文件夹,并添加我们的文件index.js 和auth.js 。
然后我们在根index.js 文件中启动我们的Express服务器,并将路由指向routes/index.js 。
接下来,我们需要在根index.js 文件中的 Prisma Client。
const express = require('express');
require('@prisma/client');
const app = express();
require('dotenv').config();
const route = require('./routes');
const bodyParser = require('body-parser');
const multer = require('multer');
app.use(bodyParser.urlencoded({ extended: true }))
app.use(bodyParser.json())
// redirect to routes/index.js
const route = require('./routes');
app.use('/', route);
const port = process.env.PORT || 5000;
app.listen(port, () => {
console.log(`server is running on port ${port}`);
});
索引路由
当这一切完成后,我们就可以继续前进,将我们的路由指向routes/index.js 文件中的各个目的地。
const express = require('express');
const router = express.Router();
const auth = require('./auth');
const createError = require('http-errors')
router.get('/', (req, res) => {
res.send('Hello World!');
});
router.use('/auth', auth);
router.use( async (req, res, next) => {
next(createError.NotFound('Route not Found'))
})
router.use( (err, req, res, next) => {
res.status(err.status || 500).json({
status: false,
message: err.message
})
})
module.exports = router;
如果你注意到,我在我的index.js 文件中需要http-errors 包。这是因为我将使用它来拦截错误,并将其作为一个消息正确地传递给客户端。
为了利用http-errors ,你可以使用安装。
npm install http-errors
创建一个授权服务
我们将需要创建一个服务文件,在我们的数据库和控制器之间进行通信。在服务文件中,我们将创建三个函数:register,login, 和all ,它们将向数据库注册一个新的用户,获得用户的信息,并将用户登录。
all 函数将获得所有的用户,这只有在请求有一个在登录或注册时产生的有效令牌时才会发生。
首先,我们将创建一个名为services 的文件夹,然后在services 文件夹内创建一个名为auth.services.js 的文件。接下来,我们可以创建我们的register 函数,并安装bcrypt和JWT,用于密码散列和生成令牌。
要安装bcrypt和JWT,请在你的终端输入以下命令并运行它。
npm install bcryptjs jsonwebtoken
安装完成后,我们将创建一个名为utils 的文件夹,以便添加我们的JWT函数,我们稍后将使用它来生成令牌。
在我们的utils 文件夹中,创建一个名为jwt.js 的文件,并添加以下函数。
const jwt = require('jsonwebtoken')
const createError = require('http-errors')
require('dotenv').config()
const accessTokenSecret = process.env.ACCESS_TOKEN_SECRET
module.exports = {
signAccessToken(payload){
return new Promise((resolve, reject) => {
jwt.sign({ payload }, accessTokenSecret, {
}, (err, token) => {
if (err) {
reject(createError.InternalServerError())
}
resolve(token)
})
})
},
verifyAccessToken(token){
return new Promise((resolve, reject) => {
jwt.verify(token, accessTokenSecret, (err, payload) => {
if (err) {
const message = err.name == 'JsonWebTokenError' ? 'Unauthorized' : err.message
return reject(createError.Unauthorized(message))
}
resolve(payload)
})
})
}
}
然后在我们的.env 文件中,我们添加我们的ACCESS_TOKEN_SECRET 。
ACCESS_TOKEN_SECRET=<CUSTOM_ACCESS_TOKEN>
然后我们可以回到auth.service.js ,要求我们的JWT文件与bcrypt和Prisma一起。
// services/auth.service.js
const { PrismaClient } = require('@prisma/client');
const prisma = new PrismaClient();
require('dotenv').config();
const bcrypt = require('bcryptjs');
const jwt = require('../utils/jwt');
接下来,创建我们的register 函数,向数据库添加一个新的用户。
class AuthService {
static async register(data) {
const { email } = data;
data.password = bcrypt.hashSync(data.password, 8);
let user = prisma.user.create({
data
})
data.accessToken = await jwt.signAccessToken(user);
return data;
}
}
module.exports = authService;
同时,我们还可以添加我们的login 和all 函数。
// services/auth.service.js
static async login(data) {
const { email, password } = data;
const user = await prisma.user.findUnique({
where: {
email
}
});
if (!user) {
throw createError.NotFound('User not registered')
}
const checkPassword = bcrypt.compareSync(password, user.password)
if (!checkPassword) throw createError.Unauthorized('Email address or password not valid')
delete user.password
const accessToken = await jwt.signAccessToken(user)
return { ...user, accessToken }
}
static async all() {
const allUsers = await prisma.user.findMany();
return allUsers;
}
创建一个授权控制器
为了从我们的路由中获得我们的请求主体,我们将创建一个名为controllers/auth.controller.js 的控制器,并添加我们的register ,login ,和all 函数来与我们各自的服务进行通信。
const auth = require('../services/auth.service');
const createError = require('http-errors');
class authController {
static register = async (req, res, next) => {
try {
const user = await auth.register(req.body);
res.status(200).json({
status: true,
message: 'User created successfully',
data: user
})
}
catch (e) {
next(createError(e.statusCode, e.message))
}
}
static login = async (req, res, next) => {
try {
const data = await auth.login(req.body)
res.status(200).json({
status: true,
message: "Account login successful",
data
})
} catch (e) {
next(createError(e.statusCode, e.message))
}
}
static all = async (req, res, next) => {
try {
const users = await auth.all();
res.status(200).json({
status: true,
message: 'All users',
data: users
})
}
catch (e) {
next(createError(e.statusCode, e.message))
}
}
}
module.exports = authController;
创建一个授权卫士
在添加了控制器后,我们可以添加我们的卫兵,这将保护一些路由,如all ,防止未登录的用户。这个守护将验证我们发布的JWT令牌,如果有效,将允许用户访问这些路由。
创建一个名为middlewares/auth.js 的文件,并添加以下代码。
const jwt = require('../utils/jwt')
const createError = require('http-errors')
const auth = async (req, res, next) => {
if (!req.headers.authorization) {
return next(createError.Unauthorized('Access token is required'))
}
const token = req.headers.authorization.split(' ')[1]
if (!token) {
return next(createError.Unauthorized())
}
await jwt.verifyAccessToken(token).then(user => {
req.user = user
next()
}).catch (e => {
next(createError.Unauthorized(e.message))
})
}
module.exports = auth;
上述代码将从routes 中添加的headers 中获取传递的令牌,以验证JWT令牌,并返回一个true 或false 。
创建一个授权路由
现在我们已经完成了我们的控制器、服务和防护。我们现在可以打开我们的routes/auth.js 文件并添加我们的路由。
const router = require('express').Router();
const user = require('../controllers/auth.controller');
const auth = require('../middlewares/auth');
// register
router.post('/', user.register);
// login
router.post('/login', user.login);
// all users
router.get('/', auth, user.all);
module.exports = router;
auth 防护被添加到all 路由中,以限制没有JWT令牌的用户进入该路由。
测试
现在我们已经完成了应用程序的构建,我们可以测试一下它是否工作正常。我们将使用Postman来测试register 、login 和all 路由。
Register
从上面的Postman截图中可以看出,一旦你输入了你的电子邮件、姓名和密码,你就成功地注册为一个新用户。
Login
当用户提供了正确的电子邮件和密码后,他们会得到一个访问令牌,他们将用来登录。在需要JWT令牌的请求中,这将作为一个头来传递。
All users
all 路径是一个受保护的路径,只针对拥有有效令牌的用户。正如你在上面的截图中看到的,令牌被添加到头的属性BearerToken 。
总结
在本教程中,我们已经经历了使用Prisma建立一个认证的服务器端应用程序的过程,用于数据库模式和迁移。然后,使用迁移后的数据,我们可以注册和登录一个用户,并创建接受有效令牌的保护路由。
对于更详细的代码库,你可以克隆存储库并进行配置。
The postCrafting authentication schemes with Prisma in Expressappeared first onLogRocket Blog.