App下載

怎么在Node.js中創(chuàng)建安全的REST API?創(chuàng)建安全的REST API的方法!

櫥窗的光 2021-08-30 17:53:16 瀏覽數(shù) (2740)
反饋

表中的內(nèi)容

  1. 介紹
  2. Node.js 對于 Rest API 的重要性!
  3. 什么是 REST,它如何與 Node.js 融合?在 Node.js 中創(chuàng)建和保護 RESTful API!創(chuàng)建您的第一個應(yīng)用 Express API創(chuàng)建用戶模塊創(chuàng)建身份驗證模塊
  4. 結(jié)論

一、簡介

應(yīng)用程序編程接口 (API) 的炒作是普遍的。它們使軟件能夠與軟件的內(nèi)部和外部部分進行交互,這是可擴展性和可重用性的基本要素。

擁有公共 API 的在線幫助現(xiàn)在非常流行。這些允許其他開發(fā)人員快速結(jié)合社交媒體登錄、信用卡債務(wù)和績效跟蹤等功能。 

他們?yōu)榇藢嵺`的標(biāo)準(zhǔn)是指定的 REpresentational State Transfer (REST),它與 ??Node.js 最佳開發(fā)技術(shù)完美配合。此外,您可以閱讀一些關(guān)于 Node.js 開發(fā)最佳實踐的最佳文章。他們可能會提供很大的幫助! 

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

Node.js不是框架或庫,而是由 Chrome 的 V8 JavaScript 引擎提供支持的運行時上下文。

作為開源項目,Node.js 由云計算和 Node.js 最佳開發(fā)提供商 Joyent 贊助。該公司資助了其他幾項技術(shù),如 Ruby on Rails 框架,并為 Twitter 和 LinkedIn 實施了托管職責(zé)。

LinkedIn 也成為首批使用 Node.js 為其移動應(yīng)用程序后端創(chuàng)建新項目的公司之一。該技術(shù)隨后被 Uber、eBay 和 Netflix 等許多技術(shù)管理員選中。

不過,直到后來,Node.js 服務(wù)器才開始廣泛使用服務(wù)器端 JavaScript。這項技術(shù)的投資在 2017 年達(dá)到頂峰,并且仍處于領(lǐng)先地位。

Node.js IDEs 是最流行的代碼編輯器,它有 JavaScript 和 Node.js 的幫助和插件,所以它只是意味著你如何根據(jù)編碼要求自定義 IDE。但是,許多 Node.js 開發(fā)人員稱贊來自 VS Code、Brackets 和 WebStorm 的特定工具。

在簡單的 Node.js 最佳開發(fā)中使用中間件是一種讓開發(fā)人員的生活更舒適的通用方法。然而,Node.js 一直是許多開發(fā)人員創(chuàng)建新的 Restful API 的最可靠來源之一。

Node.js 的力量和趨勢使它成為一場激烈的辯論。但是,您可以通過學(xué)習(xí)和探索更多 Node.js Rest API 來決定。

3. 什么是 REST 以及它如何與 Node.js 融合?

REST 是 REST API 的設(shè)計模型或設(shè)計風(fēng)格。RESTful Web 應(yīng)用程序的使用因?qū)⑵渲R呈現(xiàn)為與其支持相關(guān)的數(shù)據(jù)形式而受到贊賞。

使用 Node.js 的 REST API 還有助于其客戶在設(shè)備上執(zhí)行操作,例如替換當(dāng)前資源或設(shè)計不同的資源。

為了保護您的 RESTful API,您必須開發(fā)各種約束,而 Node.js 非常適合這一點。Node.js 服務(wù)器將設(shè)置 REST 的一組限制,以使 API 易于實踐和創(chuàng)建。 

它表明,剛開始管理您的 API 的 Nodejs 開發(fā)人員將高效快速地學(xué)習(xí)它。

此外,每當(dāng)請求使用 RESTful API 時,Node.js 服務(wù)器都會將所請求資源的狀態(tài)表示分配給客戶。

3.1 在 Node.js 中創(chuàng)建和保護 RESTful API!

正如您知道需要創(chuàng)建什么以及要求是什么一樣,這是開始創(chuàng)建應(yīng)用程序的機會。 

首先,啟動一個終端,將其轉(zhuǎn)移到您通常創(chuàng)建項目的記錄,并在那里建立一個新目錄:

mkdir express-ads-api

接下來,進入這個全新的目錄并練習(xí) npm install 來構(gòu)建一個新項目:

npm init -y

命令 over 將使用任何想要的屬性來構(gòu)建項目。如果您在文本目錄或 IDE 中啟動此目錄,您會注意到您啟動的 npm 命令形成了一個名為 package.json 的文件。違反此文件,您會發(fā)現(xiàn)如下內(nèi)容:

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"
}

在這一點上,這些數(shù)據(jù)很短,沒有那么多引人入勝的數(shù)據(jù)。然而,當(dāng)您開始計算項目的任務(wù)時,傾向于該文件將開始并變得更加令人印象深刻。

隨后,您將在設(shè)計源中建立一個名為 src 的新目錄:

mkdir src

此處的目的是將所有參考代碼放在此記錄中。因此,創(chuàng)建此目錄,在其中構(gòu)建一個名為 index.js 的不同文件,并將生成的代碼附加到其中:

// ./src/index.js 

console.log('Good Morning!'); 

保留此文件后,您可以將其定向回您的計算機并發(fā)出以下命令來試驗它:

node src

如果這按預(yù)期運行,您會注意到“早上好!” 在您的屏幕上縮進。

區(qū)域性工作的 Node.js 應(yīng)用程序“早安”console.log 信息.

3.2 創(chuàng)建你的第一個 App Express API

現(xiàn)在,您設(shè)計的項目只記錄了一條潛在消息。由于這可能不是很有價值,在創(chuàng)建您的“早安!”之后。與 Node.js 一起使用,您可以開始專注于創(chuàng)建RESTful API。 

為此,您首先需要在某些省份進行投資。因此,直接到您的計算機并宣布以下命令:

npm install body-parser cors express helmet morgan

此命令將在您的設(shè)計中建立五個依賴項:

  • body-parser:您將練習(xí)此依賴項以將傳入應(yīng)用程序的基礎(chǔ)轉(zhuǎn)換為 JavaScript 對象。
  • cors:使用此依賴項來配置 Express 以組合標(biāo)頭,聲明您的 Rest API 允許來自其他來源的請求。這被視為跨域資源共享 (CORS)。
  • express: Express 庫。
  • morgan:這個庫為您的 Express Rest API 提供了一些日志記錄功能。

在開始之前的命令后,您將在您的項目中標(biāo)記兩個項目。首先,package.json 文件將包含一個稱為依賴項的原始功能,以及之前的所有庫。 

這就是 NPM 確定項目需要哪些依賴項的方式。其次,您將在項目根目錄中看到一個名為 package-lock.json 的不同文件。 

NPM 安裝此文件以識別您在開發(fā)時練習(xí)的特定庫,因此它始終應(yīng)用相同的庫。

當(dāng) NPM 終止連接這些依賴項時,它可能會得到一些時間,根據(jù)您的互聯(lián)網(wǎng)關(guān)系,您可以啟動 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');
});

該文件的最新版本首先發(fā)送您之前建立的所有依賴項,通過不同 Express 應(yīng)用程序的生產(chǎn)和安排(const app = express())開始工作,最后提供此應(yīng)用程序偵聽端口 3001( app.listen (3001, ...))。 

此外,這段代碼代表了兩件重要的事情:

  • 一個名為 ads 的數(shù)組,簡單來說,就像一個內(nèi)存數(shù)據(jù)庫;
  • 還有一個端點,它接收 HTTP GET 應(yīng)用程序,并在觸發(fā)時提供 ads 數(shù)組的所有項目。

3.3 創(chuàng)建用戶模塊

我們將用于創(chuàng)建新項目的下一個元素是 Mongoose,它是 MongoDB 的對象數(shù)據(jù)建模 (ODM) 庫,用于在用戶模式中生成用戶指南。

為此,我們首先需要使用諸如函數(shù) req res 之類的命令來構(gòu)建 Mongoose 模式:

JavaScript:

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

確定架構(gòu)后,我們可以簡單地將架構(gòu)連接到用戶模型。

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

然后我們可以利用這個模型在我們的 Express 端點中執(zhí)行我們需要的所有 CRUD 過程。

讓我們通過在 users/routes.config.js 中找到路徑來開始“創(chuàng)建用戶”操作:

JavaScript:

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

這被引誘到主要 index.js 文件中的 Express 應(yīng)用程序中。UsersController 對象對于控制器來說是必不可少的,我們在這里適當(dāng)?shù)貏?chuàng)建一個新密碼,在 /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});
	});
};

現(xiàn)在,我們可以通過管理服務(wù)器(npm init start)并使用任何 JSON 數(shù)據(jù)將 POST 請求分配給 /users 來檢查我們的 Mongoose 模型:

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

您可以申請多種工具。Insomnia 和 Postman 是推薦的 GUI 工具,curl 是常規(guī)的 CLI 選擇。你可以練習(xí)JavaScript,即從瀏覽器內(nèi)置的開發(fā)工具控制臺日志:

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);
});

在此之后,您將找到的有效帖子的結(jié)果將是來自創(chuàng)建用戶的 ID: { "id": "1b63h8cn98w0m390" }

我們需要將 createUser 過程附加到 users/models/users.model.js 中的模型:

JavaScript:

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

所有這些步驟,現(xiàn)在我們需要查看用戶是否存在。為此,我們需要為以下端點執(zhí)行“通過 id 獲取用戶”列:users/:userId。

首先,我們在 /users/routes/config.js 中創(chuàng)建一個方法:

JavaScript:

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

之后,我們在 /users/controllers/users.controller.js 中創(chuàng)建管理器:

JavaScript:

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

最后,將 findById 方式附加到 /users/models/users.model.js 中的模型:

JavaScript:

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

您會以某種方式找到類似的響應(yīng),例如:

JSON:

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

請記住,我們可以識別散列密碼。為此,我們授予密碼,但沒有經(jīng)驗的最佳實踐是永遠(yuǎn)不要公開密碼,盡管已被散列。

我們可以識別的另一件事是權(quán)限級別,稍后我們將練習(xí)檢查用戶協(xié)議。

復(fù)制之前布局的模式,我們可以立即計算功能來刷新用戶。我們將練習(xí) PATCH 操作,因為它允許我們只轉(zhuǎn)移我們想要改進的區(qū)域。

因此,該程序?qū)?PATCH 到 /users/:userid,我們將處理我們需要開發(fā)的任何字段。

我們還需要對應(yīng)該僅限于問題中的用戶或管理員的更改執(zhí)行更多驗證,并且只有管理員可以更改權(quán)限級別。

我們將暫時離開該部分,并在安裝 auth 模塊后返回?,F(xiàn)在,控制器將顯示類似于以下內(nèi)容:

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({});
	});
};

默認(rèn)情況下,我們會發(fā)送一個沒有回復(fù)正文的 HTTP 協(xié)議代碼 204,以表明 post 請求成功。

我們需要將 patchUser 方式添加到模型中:

JSON:

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);
	})
};

相應(yīng)的程序?qū)⑹牵?br>

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);
			}
       	})
	});
};

結(jié)果列表確認(rèn)將具有以下組成:

JavaScript:

[
{
	"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({});
	});
};

類似地,如前所述,控制器將恢復(fù) HTTP 代碼 204 和無內(nèi)容材料作為確認(rèn)。

模型程序?qū)⑷缦滤荆?/p>

JavaScript:

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

我們現(xiàn)在擁有管理用戶設(shè)備所需的所有操作,我們對用戶控制器感到滿意。這段代碼的首要目的是為您提供實踐 REST API 模式的核心思想。

我們需要響應(yīng)此代碼以對其進行一些驗證和調(diào)整,但在開始時,我們希望開始構(gòu)建我們的安全性。

讓我們從 auth 模塊開始。

3.4 創(chuàng)建認(rèn)證模塊

在我們通過完成權(quán)限和驗證中間件來保護用戶模塊之前,我們需要為現(xiàn)代用戶創(chuàng)建一個強大的令牌。

我們將創(chuàng)建一個 JWT,以確認(rèn)授予正確電子郵件和身份證明的用戶。JWT 是一個特殊的 JSON Web 指示,您可以練習(xí)讓用戶安全地發(fā)出大量請求,而無需定期標(biāo)記。

它通常有一個結(jié)束時間,每隔幾次就會重新創(chuàng)建一個獨特的符號以安全地掌握信息。但是,為此,我們將避免刺激令牌并將其緩存為每次登錄時使用唯一令牌進行管理。

為此,我們將為 /auth 源的 POST 請求創(chuàng)建一個端點。請求表將包括用戶電子郵件和密碼:

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']});

}

}});

};

 這樣做之后,我們可以前進到控制器并創(chuàng)建 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});
	}
};

雖然我們不會在此更新令牌,但控制器已被修復(fù)以方便這樣的時間,以便在接下來的開發(fā)中更簡單地執(zhí)行它。

我們現(xiàn)在需要的只是在 /authorization/routes.config.js 中創(chuàng)建路徑并調(diào)用適當(dāng)?shù)闹虚g件:

JavaScript:

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

結(jié)果將在 accessToken 字段中包含創(chuàng)建的 JWT:e

JSON:

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

必須創(chuàng)建令牌,我們可以使用形式 Bearer ACCESS_TOKEN 在 Authorization 標(biāo)頭中使用它。


0 人點贊