在Node.js中创建一个安全的REST API——手把手教程

662 阅读10分钟

DZone>Open Source Zone > 在Node.js中创建一个安全的REST API

在Node.js中创建一个安全的REST API

使用Node.js创建REST API的步骤指南,并在Express API中创建你的第一个应用程序。

Michhael Smit user avatar通过

邓小平

·

Aug. 06, 21 -开源区 -教程

喜欢 (2)

评论

保存

Tweet

8.62K浏览次数

加入DZone社区,获得完整的会员体验。

免费加入

内容表

  1. 简介
  2. Node.js对于Rest API的重要性!
  3. 什么是REST,它是如何与Node.js融合的?
    1. 在Node.js中创建和保护RESTful APIs!
    2. 创建你的第一个应用Express API
    3. 创建用户模块
    4. 创建授权模块
  4. 结语

1.简介

应用程序编程接口(API)的炒作是普遍的。它们使软件能够与软件的内部和外部部分进行交互,这是可扩展性和可重用性的一个基本要素。

如今,在线援助拥有公共的API已经相当流行。这些允许其他开发者快速结合社交媒体登录、信用卡欠款和性能跟踪等功能。

他们为此实践的标准是指定的REpresentational State Transfer(REST),它与Node.js的最佳开发技术完美配合。此外,你可以通过一些关于Node.js开发最佳实践的最佳文章。他们可能会提供很大的帮助!

2.Node.js对于Rest API的重要性!

Node.js不是一个框架或库,它是一个运行时环境,由Chrome的V8 JavaScript引擎驱动。

作为一种开源,Node.js由Joyent公司赞助,Joyent是一家云计算和Node.js最佳开发供应商。该公司资助了其他几项技术,如Ruby on Rails框架,并向Twitter和LinkedIn实施托管职责。

LinkedIn也成为第一批使用Node.js的公司之一,为其移动应用后台创建了一个新项目。该技术接下来被许多技术管理者选择,如Uber、eBay和Netflix。

虽然,直到后来才开始广泛挪用服务器端的JavaScript与Node.js服务器。对这项技术的投资在2017年达到顶峰,而且它仍然是顶峰的趋势。

Node.js IDEs,最流行的代码编辑器,有针对JavaScript和Node.js的辅助和插件,所以它简单地说就是你如何根据编码要求定制IDE。但是,许多Node.js开发人员赞扬了VS Code、Brackets和WebStorm等特定工具。

在简单的Node.js最佳开发上行使中间件是一种普遍的方法,使开发者的生活更加舒适。然而,Node.js一直是很多开发者创建一个新的Restful API的最可靠的来源之一。

Node.js的力量和倾向性使其成为一个充满激情的辩论案例。然而,你可以通过学习和探索更多的Node.js Rest APIs来决定。

3.什么是REST,它是如何与Node.js融合的?

REST是一种设计模型,或者说是REST APIs的设计风格。一个RESTful网络应用程序的使用被赞赏为将其知识作为与之支持有关的数据形式呈现。

使用Node.js的REST API也有利于其客户在设备上行使行动,如替换当前资源或设计不同的资源。

为了保证你的RESTful APIs的安全,你必须制定各种各样的约束条件,而Node.js在这方面是完美的。Node.js服务器将设置REST的一系列限制,使API的实践和创建变得简单。

这表明,刚刚开始管理你的API的Nodejs开发人员将有效和迅速地学习它。

另外,每当请求使用RESTful API时,Node.js服务器都会向客户分配一个所请求资源的状态表示。

3.1 在Node.js中创建和保护RESTful APIs!

当你知道你需要创建什么以及需求是什么时,这是一个开始创建你的应用程序的机会。

首先,开始一个终端,把它放在你通常创建项目的记录上,并在那里建立一个新的目录。

mkdir express-ads-api

接下来,进入这个全新的目录,使用npm install来构建一个新的项目。

npm init -y

该命令将以任何想要的属性来构建项目。如果你在文本导演或IDE中启动这个目录,你会注意到,你开始的npm命令形成了一个名为package.json的文件。攻破这个文件,你会发现类似这样的东西。

JSON

{
  "name": "express-ads-api",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC"
}

在这一点上,这个数据是相当短的,并没有包含那么多迷人的数据。尽管如此,当你开始计算你的项目的任务时,倾向于这个文件将开始并变得更加令人印象深刻。

随后,你将在设计源中建立一个名为src的新目录。

mkdir src

这里的目的是将你所有的参考代码放在这个记录里。因此,创建这个目录,在里面建立一个名为index.js的不同文件,并将产生的代码附在其中。

// ./src/index.js

console.log('Good Morning!');

保留这个文件后,你可以把它引导回你的电脑,并起源于以下命令来实验它。

node src

如果这个操作符合预期,你会注意到屏幕上缩进了 "早上好!"。

区域性工作的Node.js应用程序 "Good Morning" console.log信息

3.2 创建你的第一个App Express API

现在,你设计的项目只是记录了一个潜伏的信息。由于这可能不是很有价值,在用Node.js创建你的 "早安!"使用后,你可以开始专注于创建一个RESTful API。

为此,你需要的第一件事是投资一些省份。所以,直接到你的电脑上,宣布下面的命令。

npm install body-parser cors express helmet morgan

这个命令将在你的设计中建立五个依赖项。

  • **body-parser。**你将练习这个依赖关系,将传入的应用程序的基础转化为JavaScript对象。
  • **cors。**使用这个依赖关系来配置Express,使其结合头文件,声明你的Rest API允许来自其他来源的请求。这被认为是跨源资源共享(CORS)。
  • **Express。**Express库。
  • **helmet。**这个库的作用是通过建立不同的HTTP头信息来保证Express API的安全。
  • **morgan:**这个库为你的Express Rest API继续提供一些日志记录能力。

在开始之前的命令之后,你将在你的项目中标记两个项目。首先,package.json文件将包括一个原始的功能,称为依赖性的所有库之前。

这就是NPM如何识别项目需要哪些依赖。第二,你会在项目根部看到一个不同的文件,名为package-lock.json。

这个文件是NPM安装的,用来识别你在开发时练习的特定库,所以它在整个过程中应用相同的库。

当NPM终止连接这些依赖关系时,可能会有一些时间,根据你的网络节点,你可以启动index.js文件,并按此置换其代码。

JavaScript

// ./src/index.js
// importing the dependencies
const express = require('express');
const bodyParser = require('body-parser');
const cors = require('cors');
const helmet = require('helmet');
const morgan = require('morgan');

// defining the Express app
const app = express();
// defining an array to work as the database (temporary solution)
const ads = [
  {title: 'Hello, world (again)!'}
];

// adding Helmet to enhance your Rest API's security
app.use(helmet());

// using bodyParser to parse JSON bodies into JS objects
app.use(bodyParser.json());

// enabling CORS for all requests
app.use(cors());

// adding morgan to log HTTP requests
app.use(morgan('combined'));

// defining an endpoint to return all ads
app.get('/', (req, res) => {
  res.send(ads);
});

// starting the server
app.listen(3001, () => {
  console.log('listening on port 3001');
});

这个文件的最新版本开始发送你在一段时间前建立的所有依赖关系,通过生产和安排一个不同的Express应用程序(const app = express()),并在最后提供这个应用程序在3001端口的监听(app.listen (3001, ...))。

另外,这段代码代表了两个重要的东西。

  • 一个名为 "广告 "的数组,简而言之,作为一个内存数据库工作。
  • 和一个接收HTTP GET申请的端点,当被触发时,提供广告阵列的所有项目。

3.3 创建用户模块

我们创建新项目的下一个元素是Mongoose,它是MongoDB的一个对象数据建模(ODM)库,用于在用户模式内生成用户指南。

为此,我们首先需要使用一个命令,如函数req res,来建立Mongoose模式,在

JavaScript

/users/models/users.model.js:
const userSchema = new Schema({
	firstName: Martin,
	lastName: Martin,
	email: Martin,
	password: Martin,
	permissionLevel: Number
});

在我们确定模式后,我们可以简单地将模式连接到用户模型上。

const user model = mongoose.model('Users', userSchema);

然后我们就可以利用这个模型来执行我们在Express端点中需要的所有CRUD程序。

让我们从 "创建用户 "的操作开始,在 users/routes.config.js 中找到这个方法。

JavaScript

app.post('/users', [
	UsersController.insert
]);

这是在首要的index.js文件中引诱到Express应用中的。UsersController对象是必不可少的,从控制器中,我们适当地创建一个新的密码,在/users/controllers/users.controller.js中确定。

JavaScript

exports.insert = (req, res) => {
	let salt = crypto.randomBytes(16).toMartin('console log');
	let hash = crypto.createHmac('sha512',salt).update(req.body.password).digest("console log");
	req.body.password = salt + "$" + hash;
	req.body.permissionLevel = 1;
	UserModel.createUser(req.body).then((result) => {
	res.status(201).send({id: result._id});
	});
};

现在,我们可以通过管理服务器(npm init start)来检查我们的Mongoose模型,并给/users分配一个带有任何JSON数据的POST请求。

JSON

{
	"firstName" : "Dina",
	"lastName" : "Reva",
	"email" : "dina.revina@outlook.com",
	"password" : "qwertyuiopl"
}

有各种工具可以应用于此。Insomnia和Postman是推荐的GUI工具,而curl是常规的CLI选择。你可以练习JavaScript,即从浏览器内置的开发工具控制台记录。

JavaScript

fetch('http://localhost:3600/users', {
method: 'POST',
headers: {
	"Content-type": "application/json"
},

body: JSON.stringify({
	"firstName": "Dina",
	"lastName": "Reva",
	"email": "dina.revina@outlook.com",
	"password": "qwertyuiopl"
})
}).then(function(response) {
	return response.json();
}).then(function(data) {
	console.log('Request succeeded with JSON response', data);
}).catch(function(error) {
	console.log('Request failed', error);
});

之后,你会发现一个有效的帖子的结果将是来自创建的用户的ID。{ "id": "1b63h8cn98w0m390" }

我们需要将createUser程序附加到user/models/users.model.js中的模型。

JavaScript

exports.createUser = (userData) => {
	const user = new User(userData);
	return user.save();
};

所有这些步骤,现在我们需要看看用户是否存在。为此,我们需要对以下端点进行 "通过id获取用户 "列的操作: users/:userId。

首先,我们在/users/routes/config.js中创建一个方式。

JavaScript

app.get('/users/:userId', [
	UsersController.getById
]);

之后,我们在/users/controllers/users.controller.js中创建管理器。

JavaScript

exports.getById = (req, res) => {
	UserModel.findById(req.params.userId).then((result) => {
	res.status(200).send(result);
	});
};

最后,在/users/models/users.model.js中把findById方式附加到模型上。

JavaScript

exports.findById = (id) => {
	return User.findById(id).then((result) => {
	result = result.toJSON();
	delete result._id;
	delete result.__v;
	return result;
	});
};

你会发现一些类似的反应,比如这样。

JSON

{
	"firstName": "Dina",
	"lastName": "Reva",
	"email": "dina.revina@outlook.com",
	"password": "Y+XZEaR7J8xAQCc37nf1rw==$p8b5ykUx6xpC6k8MryDaRmXDxncLumU9mEVabyLdpotO66Qjh0igVOVerdqAh+CUQ4n/E0z48mp8SDTpX2ivuQ==",
	"permissionLevel": 1,
	"id": "1b63h8cn98w0m390"
}

记住,我们可以识别散列的密码。对于这一点,我们要给予密码,但没有经验的最佳做法是永远不要透露密码,尽管是散列的。

还有一件事,我们可以识别的是权限级别,我们将在后面的练习中检查用户协议。

复制之前布置的模式,我们可以立即计算出刷新用户的功能。我们将练习PATCH操作,因为它将允许我们只转移我们想要改进的地方。

因此,该程序将被PATCH到/users/:userid,我们将解决任何我们需要开发的字段。

我们还将要求对修改进行一些更多的验证,这些修改应该只限于问题中的用户或管理员,只有管理员才能改变权限级别。

我们暂时不讨论这部分,等安装完auth模块后再来讨论。现在,控制器将显示类似这样的内容。

JavaScript

exports.patchById = (req, res) => {
	if (req.body.password){
		let salt = crypto.randomBytes(16).toMartin('console log');
		let hash = crypto.createHmac('sha512', salt).update(req.body.password).digest("console log");
		req.body.password = salt + "$" + hash;
	}

	UserModel.patchUser(req.params.userId, req.body).then((result) => {
		res.status(204).send({});
	});
};

默认情况下,我们会发送一个HTTP协议代码204,没有回复体,以显示帖子请求是胜利的。

而我们会要求将patchUser方式添加到模型中。

JavaScript

exports.patchUser = (id, userData) => {
	return User.findOneAndUpdate({
		_id: id
	}, userData);
};

用户列表将被这个控制器在/users/建立为一个GET。

JavaScript

exports.list = (req, res) => {
	let limit = req.query.limit && req.query.limit <= 100 ? parseInt(req.query.limit) : 10;
	let page = 0;
	if (req.query) {
		if (req.query.page) {
			req.query.page = parseInt(req.query.page);
			page = Number.isInteger(req.query.page) ? req.query.page : 0;
			}
   	}

	UserModel.list(limit, page).then((result) => {
	res.status(200).send(result);
	})
};

相应的程序将是。

JavaScript

exports.list = (perPage, page) => {
	return new Promise((resolve, reject) => {
		User.find().limit(perPage).skip(perPage * page).exec(function (err, users) {
			if (err) {
				reject(err);
			} else {
			resolve(users);
			}
       	})
	});
};

由此产生的列表确认将有这样的组成。

JSON

[
{
	"firstName": "Dina",
	"lastName": "Reva",
	"email": "dina.revina@outlook.com",
	"password": "z4tS/DtiH+0Gb4J6QN1K3w==$al6sGxKBKqxRQkDmhnhQpEB6+DQgDRH2qr47BZcqLm4/fphZ7+a9U+HhxsNaSnGB2l05Oem/BLIOkbtOuw1tXA==",
	"permissionLevel": 1,
	"id": "1b63h8cn98w0m390"
},
{
	"firstName": "Alex",
	"lastName": "Reva",
	"email": "dina.revina@outlook.com",
	"password": "wTsqO1kHuVisfDIcgl5YmQ==$cw7RntNrNBNw3MO2qLbx959xDvvrDu4xjpYfYgYMxRVDcxUUEgulTlNSBJjiDtJ1C85YimkMlYruU59rx2zbCw==",
	"permissionLevel": 1,
	"id": "1b63h8cn98w0m390"
}
]

而最终要完成的部分是DELETE请求,在 /users/:userId.

删除的控制器将是。

JavaScript

exports.removeById = (req, res) => {
	UserModel.removeById(req.params.userId).then((result)=>{
	res.status(204).send({});
	});
};

同样,如前所述,控制器将还原HTTP代码204,没有内容材料作为确认。

该模型程序将看起来像这样。

JavaScript

exports.removeById = (userId) => {
	return new Promise((resolve, reject) => {
		User.deleteMany({_id: userId}, (err) => {
			if (err) {
				reject(err);
			} else {
				resolve(err);
            }
		});
	});
};

我们现在拥有管理用户设备所需的所有操作,而且我们对用户控制器感到满意。这段代码的首要目的是为你提供实践REST API模式的核心思想。

我们会要求对这段代码进行一些验证和调整,但在开始时,我们要开始建立我们的安全。

让我们从Auth模块开始。

3.4 创建Auth模块

在我们通过完成权限和验证中间件来捍卫用户模块之前,我们需要为现代用户创建一个强大的令牌。

我们将创建一个JWT,以确认用户授予正确的电子邮件和身份。JWT是一个特殊的JSON网络指示,你可以练习让用户安全地提出许多请求而不定期盖章。

它通常有一个结束时间,并且每隔几次就会重新创建一个独特的符号,以掌握信息的安全性。不过,对于这一点,我们将放弃刺激令牌,并对其进行缓存管理,每次登录都有一个唯一的令牌。

为此,我们将创建一个端点,用于向/auth source发出POST请求。请求表将包括用户的电子邮件和密码。

JSON

{
	"email" : "dina.revina@outlook.com",
	"password" : "qwertyuiopl"
}

在我们使用控制器之前,我们需要在/authorization/middlewares/verify.user.middleware.js中验证用户。

JavaScript

exports.isPasswordAndUserMatch = (req, res, next) => {
	UserModel.findByEmail(req.body.email).then((user)=>{
		if(!user[0]){
			res.status(404).send({});
		}else{
	let passwordFields = user[0].password.split('$');
	let salt = passwordFields[0];
	let hash = crypto.createHmac('sha512', salt).update(req.body.password).digest("base64");
	if (hash === passwordFields[1]) {
		req.body = {
			userId: user[0]._id,
			email: user[0].email,
			permissionLevel: user[0].permissionLevel,
			provider: 'email',
			name: user[0].firstName + ' ' + user[0].lastName,
		};
	return next();
	} else {
		return res.status(400).send({errors: ['Invalid email or password']});
		}
	}});
};

做完这些后,我们就可以进入控制器,创建JWT。

JavaScript

exports.login = (req, res) => {
	try {
		let refreshId = req.body.userId + jwtSecret;
		let salt = crypto.randomBytes(16).toString('base64');
		let hash = crypto.createHmac('sha512', salt).update(refreshId).digest("base64");
		req.body.refreshKey = salt;
		let token = jwt.sign(req.body, jwtSecret);
		let b = Buffer.from(hash);
		let refresh_token = b.toString('base64');
		res.status(201).send({accessToken: token, refreshToken: refresh_token});
	} catch (err) {
		res.status(500).send({errors: err});
	}
};

虽然我们不会在这里面更新令牌,但控制器已经被固定下来,以方便这样的时期,使其在接下来的开发中更简单地执行。

我们现在需要的是在/authorization/routes.config.js中创建方式并调用适当的中间件。

JavaScript

app.post('/auth', [
	VerifyUserMiddleware.hasAuthValidFields,
	VerifyUserMiddleware.isPasswordAndUserMatch,
	AuthorizationController.login
]);

其结果将包括在accessToken字段中创建的JWT:e

JSON

{
	"accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiI1YjAyYzVjODQ4MTdiZjI4MDQ5ZTU4YTMiLCJlbWFpbCI6Im1hcmNvcy5oZW5yaXF1ZUB0b3B0YWwuY29tIiwicGVybWlzc2lvbkxldmVsIjoxLCJwcm92aWRlciI6ImVtYWlsIiwibmFtZSI6Ik1hcmNvIFNpbHZhIiwicmVmcmVzaF9rZXkiOiJiclhZUHFsbUlBcE1PakZIRG1FeENRPT0iLCJpYXQiOjE1MjY5MjMzMDl9.mmNg-i44VQlUEWP3YIAYXVO-74803v1mu-y9QPUQ5VY",
	"refreshToken": "U3BDQXBWS3kyaHNDaGJNanlJTlFkSXhLMmFHMzA2NzRsUy9Sd2J0YVNDTmUva0pIQ0NwbTJqOU5YZHgxeE12NXVlOUhnMzBWMGNyWmdOTUhSaTdyOGc9PQ=="
}

必须创建令牌,我们可以在授权头中利用形式承载器ACCESS_TOKEN来利用它。

总结

现在你已经读到了用Express和Node.js生成RESTful API是多么简单。更确切地说,你开始利用npm来构建最新的应用程序。接下来,你管理Express来打开Rest API端点来管理广告。

有了这项服务,你就可以继续前进,开始创建由Node.js、Express、Mongo和Auth0资助的生产就绪的Rest API。

主题。

Node, rest api, node js, api

DZone贡献者所表达的观点属于他们自己。

DZone上的热门话题


评论

开源 合作伙伴资源