[譯] 使用 Node 和 OAuth 2.0 構建一個簡單的 REST API

LeviDing發表於2018-10-01

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()
複製程式碼

utilpromisify 函式允許你接受一個期望回撥的函式,然後返回一個 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 還會在建立和更新時自動建立和更新 idcreatedAtupdatedAt 欄位。

const Part = database.define('parts', {
  partNumber: Sequelize.STRING,
  modelNumber: Sequelize.STRING,
  name: Sequelize.STRING,
  description: Sequelize.TEXT
})
複製程式碼

結語為了新增端點會請求獲取你的 Express app 訪問許可權。但 app 被定義在另一個檔案中。處理這個問題的一個方法就是匯出一個函式,該函式接受應用程式並對其進行一些操作。當我們在另一個檔案中匯入這個指令碼時,你可以像執行 initializeDatabase(app) 一樣執行它。

結語需要同時使用 appdatabase 來初始化。軟化定義你需要使用的 REST 端點。resource 函式會包括 GETPOSTPUTDELETE 動詞的端點,這些動詞大多數是自動化的。

想真正建立資料庫,你需要執行返回一個 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 欄位的值。

高亮 issuer URL。

注意:一般規則是,你不應該將 .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 命令來進行檢測:

  1. 授權報頭是否在請求之中
$ 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>
複製程式碼
  1. 在授權請求的報頭中是否有 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>
複製程式碼
  1. 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.envprocess 是一個具有大量有用變數和函式的全域性變數)。

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_IDCLIENT_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 連結。


掘金翻譯計劃 是一個翻譯優質網際網路技術文章的社群,文章來源為 掘金 上的英文分享文章。內容覆蓋 AndroidiOS前端後端區塊鏈產品設計人工智慧等領域,想要檢視更多優質譯文請持續關注 掘金翻譯計劃官方微博知乎專欄

相關文章