内容简介:JavaScript 在 web 是随处可见 —— 几乎每个 web 页面都会或多或少的包含一些 JavaScript,即使没有 JavaScript,你的浏览器也可能存在某种扩展类型向页面中注入一些 JavaScript 代码。直到如今,这些事情都不可避免。JavaScript 也可以用于浏览器的上下文之外的任何事情,从托管 web 服务器来控制 RC 汽车或运行成熟的操作系统。有时你想要几个一组无论是在本地网络还是在互联网上都可以相互交流的服务器。今天,我会向你演示如何使用 Node.js 创建一个
JavaScript 在 web 是随处可见 —— 几乎每个 web 页面都会或多或少的包含一些 JavaScript,即使没有 JavaScript,你的浏览器也可能存在某种扩展类型向页面中注入一些 JavaScript 代码。直到如今,这些事情都不可避免。
JavaScript 也可以用于浏览器的上下文之外的任何事情,从托管 web 服务器来控制 RC 汽车或运行成熟的操作系统。有时你想要几个一组无论是在本地网络还是在互联网上都可以相互交流的服务器。
今天,我会向你演示如何使用 Node.js 创建一个 REST API,并使用 OAuth 2.0 保证它的安全性,以此来阻止不必要的请求。REST API 在 web 上比比皆是,但如果没有合适的工具,就需要大量的样板代码。我会向你演示如何使用可以轻松实现客户端认证流的令人惊讶的一些工具,它可以在没有用户上下文的情况下将两台机器安全地连接。
构建你的 Node 服务器
使用 Express JavaScript 库 在 Node 中设置 web 服务器非常简单。创建一个包含服务器的新文件夹。
$ mkdir rest-api 复制代码
Node 使用 package.json
来管理依赖并定义你的项目。我们使用 npm init
来新建该文件。该命令会在帮助你初始化项目时询问你一些问题。现在你可以使用标准 JS 来强制执行编码标准,并将其用作测试。
$ cd rest-api $ npm init 这个实用 工具 将引导你创建 package.json 文件。 它只涵盖最常见的项目,并试图猜测合理的默认值。 请参阅 `npm help json` 来获取关于这些字段的确切文档以及它们所做的事情。 使用 `npm install <pkg>` 命令来安装一个 npm 依赖,并将其保存在 package.json 文件中。 Press ^C at any time to quit. package name: (rest-api) version: (1.0.0) description: A parts catalog entry point: (index.js) test command: standard git repository: keywords: author: license: (ISC) About to write to /Users/Braden/code/rest-api/package.json: { "name": "rest-api", "version": "1.0.0", "description": "A parts catalog", "main": "index.js", "scripts": { "test": "standard" }, "author": "", "license": "ISC" } Is this OK? (yes) 复制代码
默认的入口端点是 index.js
,因此,你应当创建一个 index.js
文件。下面的代码将为你提供一个出了默认监听 3000 端口以外什么也不做的非常基本的服务器。
index.js
const express = require('express') const bodyParser = require('body-parser') const { promisify } = require('util') const app = express() app.use(bodyParser.json()) const startServer = async () => { const port = process.env.SERVER_PORT || 3000 await promisify(app.listen).bind(app)(port) console.log(`Listening on port ${port}`) } startServer() 复制代码
util
的 promisify
函数允许你接受一个期望回调的函数,然后返回一个 promise,这是处理异步代码的新标准。这还允许我们使用相对较新的 async
/ await
语法,并使我们的代码看起来漂亮得多。
为了让它运行,你需要下载你在文件头部导入的 require
依赖。使用 npm install
来安装他们。这会将一些元数据自动保存到你的 package.json
文件中,并将它们下载到本地的 node_modules
文件中。
注意:你永远都不应该向源代码提交 node_modules
,因为对于源代码的管理,往往会很快就变得臃肿,而 package-lock.json
文件将跟踪你使用的确切版本,如果你将其安装在另一台计算机上,它们将得到相同的代码。
$ npm install express@4.16.3 util@0.11.0 复制代码
对于一些快速 linting,请安装 standard
作为 dev 依赖,然后运行它以确保你的代码达到标准。
$ npm install --save-dev standard@11.0.1 $ npm test > rest-api@1.0.0 test /Users/bmk/code/okta/apps/rest-api > standard 复制代码
如果一切顺利,在 > standard
线后,你不应该看到任何输出。如果有错误,可能如下所示:
$ npm test > rest-api@1.0.0 test /Users/bmk/code/okta/apps/rest-api > standard standard: Use JavaScript Standard Style (https://standardjs.com) standard: Run `standard --fix` to automatically fix some problems. /Users/Braden/code/rest-api/index.js:3:7: Expected consistent spacing /Users/Braden/code/rest-api/index.js:3:18: Unexpected trailing comma. /Users/Braden/code/rest-api/index.js:3:18: A space is required after ','. /Users/Braden/code/rest-api/index.js:3:38: Extra semicolon. npm ERR! Test failed. See above for more details. 复制代码
现在,你的代码已经准备好了,也下载了所需的依赖,你可以用 node .
运行服务器了。( .
表示查看前目录,然后检查你的 package.json
文件,以确定该目录中使用的主文件是 index.js
):
$ node . Listening on port 3000 复制代码
为了测试它的工作状态,你可以使用 curl
命令。没有终结点,所以 Express 将返回一个错误:
$ curl localhost:3000 -i HTTP/1.1 404 Not Found X-Powered-By: Express Content-Security-Policy: default-src 'self' X-Content-Type-Options: nosniff Content-Type: text/html; charset=utf-8 Content-Length: 139 Date: Thu, 16 Aug 2018 01:34:53 GMT Connection: keep-alive <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title>Error</title> </head> <body> <pre>Cannot GET /</pre> </body> </html> 复制代码
即使它报错,那也是非常好的情况。你还没有设置任何端点,因此 Express 唯一要返回的是 404 错误。如果你的服务器根本没有运行,你将得到如下错误:
$ curl localhost:3000 -i curl: (7) Failed to connect to localhost port 3000: Connection refused 复制代码
用 Express、Sequelize 和 Epilogue 构建你的 REST API
你现在有了一台正在运行的 Express 服务器,你可以添加一个 REST API。这实际上比你想象中的简单的多。我看过的最简单的方法是使用Sequelize 来定义数据库字段, Epilogue 创建带有接近零样板的 REST API 端点。
你需要将这些依赖加入到你的项目中。Sequelize 也需要知道如何与数据库进行通信。现在,使用 SQLite 是因为它能帮助我们快速地启动和运行。
npm install sequelize@4.38.0 epilogue@0.7.1 sqlite3@4.0.2 复制代码
新建一个包含以下代码的文件 database.js
。我会在下面详细解释每一部分。
database.js
const Sequelize = require('sequelize') const epilogue = require('epilogue') const database = new Sequelize({ dialect: 'sqlite', storage: './test.sqlite', operatorsAliases: false }) const Part = database.define('parts', { partNumber: Sequelize.STRING, modelNumber: Sequelize.STRING, name: Sequelize.STRING, description: Sequelize.TEXT }) const initializeDatabase = async (app) => { epilogue.initialize({ app, sequelize: database }) epilogue.resource({ model: Part, endpoints: ['/parts', '/parts/:id'] }) await database.sync() } module.exports = initializeDatabase 复制代码
你现在只需要将那些文件导入主应用程序并运行初始化函数即可。在你的 index.js
文件中添加以下内容。
index.js
@@ -2,10 +2,14 @@ const express = require('express') const bodyParser = require('body-parser') const { promisify } = require('util') +const initializeDatabase = require('./database') + const app = express() app.use(bodyParser.json()) const startServer = async () => { + await initializeDatabase(app) + const port = process.env.SERVER_PORT || 3000 await promisify(app.listen).bind(app)(port) console.log(`Listening on port ${port}`) 复制代码
你现在可以测试语法错误,如果一切 看上去都正常了,就可以启动应用程序了:
$ npm test && node . > rest-api@1.0.0 test /Users/bmk/code/okta/apps/rest-api > standard Executing (default): CREATE TABLE IF NOT EXISTS `parts` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `partNumber` VARCHAR(255), `modelNu mber` VARCHAR(255), `name` VARCHAR(255), `description` TEXT, `createdAt` DATETIME NOT NULL, `updatedAt` DATETIME NOT NULL); Executing (default): PRAGMA INDEX_LIST(`parts`) Listening on port 3000 复制代码
在另一个终端,你可以测试它是否实际上已经在工作了(我使用 json CLI 来格式化 JSON 响应,使用 npm install --global json
进行全局安装):
$ curl localhost:3000/parts [] $ curl localhost:3000/parts -X POST -d '{ "partNumber": "abc-123", "modelNumber": "xyz-789", "name": "Alphabet Soup", "description": "Soup with letters and numbers in it" }' -H 'content-type: application/json' -s0 | json { "id": 1, "partNumber": "abc-123", "modelNumber": "xyz-789", "name": "Alphabet Soup", "description": "Soup with letters and numbers in it", "updatedAt": "2018-08-16T02:22:09.446Z", "createdAt": "2018-08-16T02:22:09.446Z" } $ curl localhost:3000/parts -s0 | json [ { "id": 1, "partNumber": "abc-123", "modelNumber": "xyz-789", "name": "Alphabet Soup", "description": "Soup with letters and numbers in it", "createdAt": "2018-08-16T02:22:09.446Z", "updatedAt": "2018-08-16T02:22:09.446Z" } ] 复制代码
这发生了什么?
如果你之前一直是按照我们的步骤来的,那么是可以跳过这部分的,因为这部分是我之前承诺过要给出的解释。
Sequelize
函数创建了一个数据库。这是配置详细信息的地方,例如要使用 SQL 语句。现在,使用 SQLite 来快速启动和运行。
const database = new Sequelize({ dialect: 'sqlite', storage: './test.sqlite', operatorsAliases: false }) 复制代码
一旦创建了数据库,你就可以为每个表使用 database.define
来定义它的表。用一些有用的字段创建叫做 parts
的表来进跟踪 parts。默认情况下,Sequelize 还会在创建和更新时自动创建和更新 id
、 createdAt
和 updatedAt
字段。
const Part = database.define('parts', { partNumber: Sequelize.STRING, modelNumber: Sequelize.STRING, name: Sequelize.STRING, description: Sequelize.TEXT }) 复制代码
结语为了添加端点会请求获取你的 Express app
访问权限。但 app
被定义在另一个文件中。处理这个问题的一个方法就是导出一个函数,该函数接受应用程序并对其进行一些操作。当我们在另一个文件中导入这个脚本时,你可以像运行 initializeDatabase(app)
一样运行它。
结语需要同时使用 app
和 database
来初始化。软化定义你需要使用的 REST 端点。 resource
函数会包括 GET
、 POST
、 PUT
和 DELETE
动词的端点,这些动词大多数是自动化的。
想真正创建数据库,你需要运行返回一个 promise 的 database.sync()
。在你启动服务器之前,你需要等待它执行结束。
module.exports
意思是 initializeDatabase
函数可以从另一个函数中导入。
const initializeDatabase = async (app) => { epilogue.initialize({ app, sequelize: database }) epilogue.resource({ model: Part, endpoints: ['/parts', '/parts/:id'] }) await database.sync() } module.exports = initializeDatabase 复制代码
用 OAuth 2.0 保护你的 Node + Express REST API
现在你已经启动并运行了 REST API,想象你希望一个特定的应用程序从远程位置使用这个 API。如果你把它按照原样存放在互联网上,那么任何人都可以随意添加、修改或删除部位。
为了避免这个情况,你可以使用 OAuth 2.0 客户端凭证。这是一种不需要上下文就可以让两个服务器相互通信的方式。这两个服务器必须事先同意使用第三方授权服务器。假设有两个服务器,A 和 B,以及一个接权服务器。服务器 A 托管 REST API,服务器 B 希望访问该 API。
- 服务器 B 向授权服务器发送一个私钥来证明自己的身份,并申请一个临时令牌。
- 服务器 B 会向往常一样使用 REST API,但会将令牌与请求一起发送。
- 服务器 A 向授权服务器请求一些元数据,这些元数据可用于验证令牌。
- 服务器 A 验证服务器 B 的请求。
- 如果它是有效的,一个成功的响应将被发送并且服务器 B 正常运行。
- 如果令牌无效,则将发送错误消息,并且不会泄露敏感信息。
创建授权服务器
这就是 OKta 发挥作用的地方。OKta 可以扮演允许你保护数据的服务器的角色。你可能会问自己“为什么是 OKta?”好的,对于构建 REST 应用程序来说,它非常的酷,但是构建一个 安全 的应用程序会更酷。为了实现这个目标,你需要添加身份验证,以便用户在查看/修改组之前必须要登录才可以。在 Okta 中,我们的目标是确保身份管理比你过去使用的要更容易、更安全、更可扩展。Okta 是一种云服务,它允许开发者创建、编辑和安全存储用户账户以及用户账户数据,并将它们与一个或多个应用程序连接。我们的 API 允许你:
- 验证 并授权你的用户
- 存储关于用户的数据
- 允许基于密码和社交的登录方式
- 使用多个代理身份验证来保护你的应用程序
- 还有更多!查看我们的产品文档
如果你还没有账户, 可以注册一个永久免费的开发者账号 ,让我们开始吧!
创建账户后,登录到开发者控制台,导航到 API ,然后导航到 Authorization Servers 选项卡。单击 default
服务器的链接。
从这个 Settings 选项卡中,复制 Issuer
字段。你需要把它保存在你的 Node 应用程序可以阅读的地方。在你的项目中,创建一个名为 .env
的文件,如下所示:
.env
ISSUER=https://{yourOktaDomain}/oauth2/default 复制代码
ISSUER
的值应该是设置页面的 Issuer URI
字段的值。
注意:一般规则是,你不应该将 .env
文件存储在源代码管理中。这允许多个项目同时使用相同的源代码,而不是需要单独的分支。它确保你的安全信息不会被公开(特备是如果你将代码作为开源发布时)。
接下来,导航到 Scopes 菜单。单击 Add Scope 按钮,然后为 REST API 创建一个作用域。你需要起一个名称(例如, parts_manager
),如果你愿意,还可以给它一个描述。
你还应该将作用域添加到你的 .env
文件中,以便你的代码可以访问到它。
.env
ISSUER=https://{yourOktaDomain}/oauth2/default SCOPE=parts_manager 复制代码
你现在需要创建一个客户端。导航到 Applications ,然后单击 Add Application 。选择 Service ,然后单击 Next 。输入服务名(例如 Parts Manager
)然后单击 Done 。
这将带你到具体的客户凭据的页面。这些是服务器 B(将消耗 REST API 的服务器)为了进行身份验证所需要的凭据。在本例中,客户端和服务器代码位于同一存储库中,因此继续将这些数据添加到你的 .env
文件中。请确保将 {yourClientId}
和 {yourClientSecret}
替换为此页面中的值。
CLIENT_ID={yourClientId} CLIENT_SECRET={yourClientSecret} 复制代码
创建中间件来验证 Express 中的令牌
在 Express 中,你可以添加将在每个端点之前运行的中间件。然后可以添加元数据,设置报头,记录一些信息,甚至可以提前取消请求并发送错误消息。在本例中,你需要创建一些中间件来验证客户端发送的令牌。如果令牌是有效的,它会被送达至 REST API 并返回适当的响应。如果令牌无效,它将使用错误消息进行响应,这样只有授权的机器才能访问。
想要验证令牌,你尅使用 OKta 的中间件。你还需要一个叫做 dotenv 的工具来加载环境变量:
npm install dotenv@6.0.0 @okta/jwt-verifier@0.0.12 复制代码
现在创建一个叫做 auth.js
的文件,它可以导出中间件:
auth.js
const OktaJwtVerifier = require('@okta/jwt-verifier') const oktaJwtVerifier = new OktaJwtVerifier({ issuer: process.env.ISSUER }) module.exports = async (req, res, next) => { try { const { authorization } = req.headers if (!authorization) throw new Error('You must send an Authorization header') const [authType, token] = authorization.trim().split(' ') if (authType !== 'Bearer') throw new Error('Expected a Bearer token') const { claims } = await oktaJwtVerifier.verifyAccessToken(token) if (!claims.scp.includes(process.env.SCOPE)) { throw new Error('Could not verify the proper scope') } next() } catch (error) { next(error.message) } } 复制代码
函数首先会检查 authorization
报头是否在该请求中,然后抛出一个错误。如果存在,它应该类似于 Bearer {token}
,其中 {token}
是一个JWT 字符串。如果报头不是以 Bearer
开头,则会引发另一个错误。然后我们将令牌发送到 Okta 的 JWT 验证器 来验证令牌。如果令牌无效,JWT 验证器将抛出一个错误,否则,它将返回一个带有一些信息的对象。然后你可以验证要求是否包含你期望的范围。
如果一切顺利,它就会以无参的形式调用 next()
函数,这将告诉 Express 可以转到链中的下一个函数(另一个中间件或最终端点)。如果将字符串传递给 next
函数,那么 Express 将其视为将被传回客户端的错误,并且不会在链中继续。
你仍然需要导入这个函数并将其作为中间件添加到应用程序中。你还需要在索引文件的顶部加载 dotenv
,以确保 .env
中的环境变量加载到你的应用程序中。对 index.js
作以下更改:
index.js
@@ -1,11 +1,14 @@ +require('dotenv').config() const express = require('express') const bodyParser = require('body-parser') const { promisify } = require('util') +const authMiddleware = require('./auth') const initializeDatabase = require('./database') const app = express() app.use(bodyParser.json()) +app.use(authMiddleware) const startServer = async () => { await initializeDatabase(app) 复制代码
如果测试请求是否被正确阻止,请尝试再次运行...
$ npm test && node . 复制代码
然后在另一个终端上运行一些 curl
命令来进行检测:
- 授权报头是否在请求之中
$ curl localhost:3000/parts <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title>Error</title> </head> <body> <pre>You must send an Authorization header</pre> </body> </html> 复制代码
- 在授权请求的报头中是否有 Bearer 令牌
$ curl localhost:3000/parts -H 'Authorization: Basic asdf:1234' <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title>Error</title> </head> <body> <pre>Expected a Bearer token</pre> </body> </html> 复制代码
- Bearer 令牌是否有效
$ curl localhost:3000/parts -H 'Authorization: Bearer asdf' <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title>Error</title> </head> <body> <pre>Jwt cannot be parsed</pre> </body> </html> 复制代码
在 Node 中创建一个测试客户端
你现在已经禁止没有有效令牌的人访问应用程序,但如何获取令牌并使用它呢?我会向你演示如何在 Node 中编写一个简单的客户端,这也将帮助你测试一个有效令牌的工作。
npm install btoa@1.2.1 request-promise@4.2.2 复制代码
client.js
require('dotenv').config() const request = require('request-promise') const btoa = require('btoa') const { ISSUER, CLIENT_ID, CLIENT_SECRET, SCOPE } = process.env const [,, uri, method, body] = process.argv if (!uri) { console.log('Usage: node client {url} [{method}] [{jsonData}]') process.exit(1) } const sendAPIRequest = async () => { const token = btoa(`${CLIENT_ID}:${CLIENT_SECRET}`) try { const auth = await request({ uri: `${ISSUER}/v1/token`, json: true, method: 'POST', headers: { authorization: `Basic ${token}` }, form: { grant_type: 'client_credentials', scope: SCOPE } }) const response = await request({ uri, method, body, headers: { authorization: `${auth.token_type} ${auth.access_token}` } }) console.log(response) } catch (error) { console.log(`Error: ${error.message}`) } } sendAPIRequest() 复制代码
这里,代码将 .env
中的变量加载到环境中,然后从 Node 中获取它们。节点将环境变量存储在 process.env
( process
是一个具有大量有用变量和函数的全局变量)。
require('dotenv').config() // ... const { ISSUER, CLIENT_ID, CLIENT_SECRET, SCOPE } = process.env // ... 复制代码
接下来,由于这将从命令行运行,所以你可以再次使用 process
来获取与 process.argv
一起传入的参数。这将为你提供一个数组,其中包含传入的所有参数。前两个逗号前面没有任何变量名称,因为在本例中前两个不重要;他们只是通向 node
的路径,以及脚本的名称( client
或者 client.js
)。
URL 是必须的,它包括端点,但是方法和 JSON 数据是可选的。默认的方法是 GET
,因此如果你只是获取数据,就可以忽略它。在这种情况下,你也不需要任何有效负载。如果参数看起来不正确,那么这将使用错误消息和退出代码 1
退出程序,这表示错误。
const [,, uri, method, body] = process.argv if (!uri) { console.log('Usage: node client {url} [{method}] [{jsonData}]') process.exit(1) } 复制代码
Node 当前不允许在主线程中使用 await
,因此要使用更干净的 async
/ await
语法,你必须创建一个函数,然后调用它。
如果在任何一个 await
函数中发生错误,那么屏幕上就会打印出 try
/ catch
。
const sendAPIRequest = async () => { try { // ... } catch (error) { console.error(`Error: ${error.message}`) } } sendAPIRequest() 复制代码
这是客户端向授权服务器发送令牌请求的地方。对于授权服务器本身的授权,你需要使用 Basic Auth。当你得到一个内置弹出要求用户名和密码时,基本认证是浏览器使用相同的行为。假设你的用户名是 AzureDiamond
并且你的密码是 hunter2
。你的浏览器就会将它们用( :
)连起来,然后 base64(这就是 btoa
函数的功能)对它们进行编码,来获取 QXp1cmVEaWFtb25kOmh1bnRlcjI=
。然后它发送一个授权报头 Basic QXp1cmVEaWFtb25kOmh1bnRlcjI=
。服务器可以用 base64 对令牌进行解码,以获取用户名和密码。
基础授权本身并不安全,因为它很容易被破解,这就是为什么 https
对于中间人攻击很重要。在这里,客户端 ID 和客户端密钥分别是用户名和密码。这也是为什么必须保证 CLIENT_ID
和 CLIENT_SECRET
是私有的原因。
对于 OAuth 2.0,你还需要制定授权类型,在本例中为 client_credentials
,因为你计划在两台机器之间进行对话。你还要指定作用域。还有需要其他选项需要在这里进行添加,但这是我们这个示例所需要的所有选项。
const token = btoa(`${CLIENT_ID}:${CLIENT_SECRET}`) const auth = await request({ uri: `${ISSUER}/v1/token`, json: true, method: 'POST', headers: { authorization: `Basic ${token}` }, form: { grant_type: 'client_credentials', scope: SCOPE } }) 复制代码
一旦你通过验证,你就会得到一个访问令牌,你可以将其发送到 REST API,改令牌应该类似于 Bearer eyJra...HboUg
(实际令牌要长的多 —— 可能在 800 个字符左右)。令牌包含 REST API 需要的所有信息,可以验证令牌的失效时间以及各种其他信息,像请求作用域、发出者和用于令牌的客户端 ID。
来自 REST API 的响应就会打印在屏幕上。
const response = await request({ uri, method, body, headers: { authorization: `${auth.token_type} ${auth.access_token}` } }) console.log(response) 复制代码
现在就尝试一下。同样,用 npm test && node .
启动你的应用程序,然后尝试一些像下面的命令:
$ node client http://localhost:3000/parts | json [ { "id": 1, "partNumber": "abc-123", "modelNumber": "xyz-789", "name": "Alphabet Soup", "description": "Soup with letters and numbers in it", "createdAt": "2018-08-16T02:22:09.446Z", "updatedAt": "2018-08-16T02:22:09.446Z" } ] $ node client http://localhost:3000/parts post '{ "partNumber": "ban-bd", "modelNumber": 1, "name": "Banana Bread", "description": "Bread made from bananas" }' | json { "id": 2, "partNumber": "ban-bd", "modelNumber": "1", "name": "Banana Bread", "description": "Bread made from bananas", "updatedAt": "2018-08-17T00:23:23.341Z", "createdAt": "2018-08-17T00:23:23.341Z" } $ node client http://localhost:3000/parts | json [ { "id": 1, "partNumber": "abc-123", "modelNumber": "xyz-789", "name": "Alphabet Soup", "description": "Soup with letters and numbers in it", "createdAt": "2018-08-16T02:22:09.446Z", "updatedAt": "2018-08-16T02:22:09.446Z" }, { "id": 2, "partNumber": "ban-bd", "modelNumber": "1", "name": "Banana Bread", "description": "Bread made from bananas", "createdAt": "2018-08-17T00:23:23.341Z", "updatedAt": "2018-08-17T00:23:23.341Z" } ] $ node client http://localhost:3000/parts/1 delete | json {} $ node client http://localhost:3000/parts | json [ { "id": 2, "partNumber": "ban-bd", "modelNumber": "1", "name": "Banana Bread", "description": "Bread made from bananas", "createdAt": "2018-08-17T00:23:23.341Z", "updatedAt": "2018-08-17T00:23:23.341Z" } ] 复制代码
了解更多关于 Okta 的 Node 和 OAuth 2.0 客户端凭据的更多信息
希望你已经看到了在 Node 中创建 REST API 并对未经授权的用户进行安全保护是多么容易的。现在,你已经有机会创建自己的示例项目了,请查看有关 Node、OAuth 2.0 和 Okta 的其他一些优秀资源。你还可以浏览 Okta 开发者博客,以获取其他优秀文章。
和以前一样,你可以在下面的评论中或在 Twitter@oktadev 给我们提供反馈或者提问,我们期待收到你的来信!
如果发现译文存在错误或其他需要改进的地方,欢迎到 掘金翻译计划 对译文进行修改并 PR,也可获得相应奖励积分。文章开头的 本文永久链接 即为本文在 GitHub 上的 MarkDown 链接。
掘金翻译计划 是一个翻译优质互联网技术文章的社区,文章来源为掘金 上的英文分享文章。内容覆盖 Android 、 iOS 、 前端 、 后端 、 区块链 、 产品 、 设计 、 人工智能 等领域,想要查看更多优质译文请持续关注 掘金翻译计划 、官方微博、 知乎专栏 。
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- 在 Android Studio 里使用构建分析器提升构建性能
- 使用 Docker 构建
- 使用 webpack 构建应用
- 使用Dockerfile构建镜像
- 使用模式构建:总结
- 使用 AutoAI 自动构建模型
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Linux/Unix设计思想
甘卡兹 / 漆犇 / 人民邮电出版社 / 2012-3-28 / 39.00元
《Linux\Unix设计思想/图灵程序设计丛书》内容简介:将Linux的开发方式与Unix的原理有效地结合起来,总结出Linux与Unix软件开发中的设计原则。《Linux\Unix设计思想/图灵程序设计丛书》前8章分别介绍了Linux与Unix中9条基本的哲学准则和10条次要准则。第9章和第10章将Unix系统的设计思想与其他系统的设计思想进行了对比。最后介绍了Unix哲学准则在其他领域中的应......一起来看看 《Linux/Unix设计思想》 这本书的介绍吧!
RGB转16进制工具
RGB HEX 互转工具
XML、JSON 在线转换
在线XML、JSON转换工具