JavaScript >> Javascript 文檔 >  >> Node.js

如何在 Node.js 中生成簽名的 Amazon S3 URL

使用短期簽名 URL 訪問 Amazon S3 存儲桶中的私有內容。

開始使用

為了加快我們的工作,我們將使用 CheatCode Node.js Boilerplate 作為我們工作的起點。首先,讓我們克隆該項目的副本:

終端

git clone https://github.com/cheatcode/nodejs-server-boilerplate.git

接下來,我們需要安裝樣板的依賴:

終端

cd nodejs-server-boilerplate && npm install

在此之後,我們需要安裝 aws-sdk 來自 NPM 的包,這將使我們能夠訪問適用於 Node.js 的 Amazon S3 API:

終端

npm i aws-sdk

最後,啟動開發服務器:

終端

npm run dev

運行之後,我們就可以開始了。

編寫生成簽名 URL 的函數

幸運的是,aws-sdk 作為 S3 的一部分,庫為我們提供了一個簡單的函數 用於生成簽名 URL 的構造函數。我們要做的是編寫一個函數來包裝它並初始化我們與 Amazon S3 的連接。

/lib/getSignedS3URL.js

import AWS from "aws-sdk";
import settings from "./settings";

AWS.config = new AWS.Config({
  accessKeyId: settings?.aws?.akid,
  secretAccessKey: settings?.aws?.sak,
  region: "us-east-1",
  signatureVersion: "v4",
});

const s3 = new AWS.S3();

在我們導入 aws-sdk 之後 頂部為 AWS ,我們設置全局AWS.config 值等於 AWS.Config 的新實例 類(注意小寫 cd 之間的細微差別 在我們設置的全局和大寫字母 C 關於構造函數)。

對於該類,我們傳遞一個具有一些不同設置的對象。首先,我們要注意accessKeyIdsecretAccessKey 特性。這些設置為我們從 AWS 獲得的密鑰,這些密鑰將我們對 S3 的調用與我們的 AWS 賬戶相關聯。

雖然獲取這些密鑰超出了本教程的範圍,但如果您還沒有它們,請閱讀本官方指南,了解如何通過 AWS IAM(身份訪問管理)創建它們。

獲得密鑰後,您可以繼續本教程。

在上面的代碼中,我們不是 將我們的密鑰直接粘貼到我們的代碼中。相反,我們使用 settings 我們正在使用的樣板中內置的功能。它被設置為在每個環境的基礎上為我們的應用加載設置(即,為我們的 development 加載不同的鍵 環境與我們的 production 環境)。

我們這裡導入的文件(位於/lib/settings.js ) 負責決定我們的應用啟動時需要加載哪些設置文件(由 npm run dev 啟動的進程 我們之前運行的命令)。默認情況下,樣板文件包含 settings-development.json 項目根目錄下的文件,該文件旨在包含我們的開發 環境密鑰(將您的密鑰與環境分開可防止不必要的錯誤和安全問題)。

打開該文件,我們要添加您獲得的 AWS 密鑰,如下所示:

/settings-development.json

{
  [...]
  "aws": {
    "akid": "",
    "sak": ""
  },
  [...]
}

在這裡,我們在名為 aws 的文件根目錄處的 JSON 對像中按字母順序添加一個新屬性 (因為我們在 .json 文件,我們需要使用雙引號)。設置到該屬性的是另一個包含我們來自 AWS 的密鑰的對象。這裡,akid 應將其值設置為您的 IAM 用戶和 sak 的訪問密鑰 ID 應該將其值設置為您的秘密訪問密鑰。

/lib/getSignedS3URL.js

import AWS from "aws-sdk";
import settings from "./settings";

AWS.config = new AWS.Config({
  accessKeyId: settings?.aws?.akid,
  secretAccessKey: settings?.aws?.sak,
  region: "us-east-1",
  signatureVersion: "v4",
});

const s3 = new AWS.S3();

回到我們的文件,使用 settings 導入,現在我們可以用 settings.aws.akid 指向我們的鍵 和 settings.aws.sak . ? 在上面的每個屬性之間是一種速記技術,可以幫助我們避免寫出 settings && settings.aws && settings.aws.akidsettings?.aws?.akid 我們在上面看到的等價於這個)。

設置好鍵後,接下來,我們確保設置 region 我們的 Amazon S3 存儲桶所在的位置。創建 S3 存儲桶也超出了本教程的範圍,因此,如果您尚未設置,請閱讀 AWS 的本指南,然後在完成後繼續本教程。請務必記下您創建存儲桶的區域(如果您找不到該區域的虛線版本,請查看此列表以找到要傳遞給 region 的正確代碼 高於 looks-like-this )。

接下來,使用您的 region 設置,我們添加 signatureVersion ,將其設置為 v4 (這是AWS簽名協議的最新版本)。

最後,為了完善上面的代碼片段,一旦我們將所有設置傳遞給 AWS.Config ,我們創建一個變量const s3 並將其設置為 AWS.S3() 的新實例 類。

/lib/generateSignedS3URL.js

import AWS from "aws-sdk";
import settings from "./settings";

AWS.config = new AWS.Config({ ... });

const s3 = new AWS.S3();

export default ({ bucket, key, expires }) => {
  const signedUrl = s3.getSignedUrl("getObject", {
    Key: key,
    Bucket: bucket,
    Expires: expires || 900, // S3 default is 900 seconds (15 minutes)
  });

  return signedUrl;
};

就像我們之前暗示的那樣,aws-sdk 庫使生成簽名 URL 變得相當簡單。在這裡,我們添加了一個我們設置為默認 export 的函數 .我們希望該函數將單個參數作為具有三個屬性的 JavaScript 對象接收:

  1. bucket - 保存我們要為其檢索簽名 URL 的文件(AWS 中的“對象”)的 S3 存儲桶。
  2. key - 我們 S3 存儲桶中文件或“對象”的路徑。
  3. expires - 我們希望 URL 可訪問多長時間(在此持續時間之後,後續嘗試使用該 URL 將失敗)。

在函數內部,我們創建了一個新變量 const signedUrl 我們希望包含我們的 signedUrl ,在這裡,我們期望從調用 s3.getSignedUrl() 得到什麼 . .getSignedUrl() 的獨特之處 這裡的方法是它是 同步的 .這意味著當我們調用該函數時,JavaScript 將等待它返回一個值給我們,然後再評估我們的其餘代碼。

向該函數傳遞兩個參數:我們要執行的 S3 操作(getObjectputObject ) 和一個選項對象,該對象描述了我們要為其檢索簽名 URL 的文件。

這裡應該解釋一下操作。這裡,getObject 表示“我們希望為 S3 存儲桶中的現有對象獲取簽名 URL。”如果我們將其更改為 putObject ,我們可以同時創建 一個新對象 取回它的簽名 URL。如果您總是需要取回已簽名的 URL(而不是在文件已上傳後獲取),這將非常方便。

對於選項對象,在這裡,我們只是從傳遞給包裝函數的參數中復制屬性。您會注意到對像上的屬性傳遞給 .getSignedUrl() 是大寫的,而傳遞給我們的包裝函數的是小寫。在 aws-sdk , 大寫字母用於傳遞給庫中函數的選項。在這裡,我們為包裝函數使用小寫字母以使事情更簡單。

為了安全起見,對於 Expires 選項,如果我們沒有通過自定義 expires 值到我們的包裝函數中,我們回退到 900 秒或 15 分鐘(這意味著我們從亞馬遜返回的 URL 只能在 15 分鐘內訪問,然後才會失效)。

最後,為了結束我們的函數,我們返回 signedUrl .接下來,為了測試這一點,我們將設置一個簡單的 Express.js 路由,我們可以在其中調用該函數。

連接 Express 路由以測試 URL 生成

作為本教程使用的 CheatCode Node.js 樣板的一部分,我們提供了一個預先配置的 Express.js 服務器。該服務器是在 /index.js 內部創建的 在項目的根目錄。在那裡,我們創建 Express app 然後——為了保持井井有條——傳遞 app 實例化為一系列函數,我們在其中定義我們的實際路由(或擴展 Express HTTP 服務器)。

/api/index.js

import getSignedS3URL from "../lib/getSignedS3URL";
import graphql from "./graphql/server";

export default (app) => {
  graphql(app);

  app.use("/s3/signed-url", (req, res) => {
    const signedUrl = getSignedS3URL({
      bucket: "cheatcode-tutorials",
      key: "panda.jpeg",
      expires: 5, // NOTE: Make this URL expire in five seconds.
    });

    res.send(`
      <html>
        <head>
          <title>AWS Signed URL Test</title>
        </head>
        <body>
          <p>URL on Amazon: ${signedUrl}</p>
          <img src="${signedUrl}" alt="AWS Signed URL Test" />
          <script>
            setTimeout(() => {
              location = "${signedUrl}";
            }, 6 * 1000);
          </script>
        </body>
      </html>
    `);
  });
};

這裡,在 api() 裡面 從 /index.js 調用的函數 我們剛剛討論的文件,我們採用 Express app 實例作為參數。默認情況下,樣板文件為我們設置了一個 GraphQL 服務器,在這裡,我們將該服務器的創建分離到它自己的函數 graphql() , 傳入 app 實例,因此可以在內部引用。

接下來,我們在本教程中關心的部分,我們在 /s3/signed-url 處創建一個測試路由 在我們的應用程序中(我們的服務器正在運行,這將在 http://localhost:5001/s3/signed-url )。在該路由的回調中,我們可以看到對 getSignedS3URL() 的調用 函數(要清楚,我們的包裝函數)。給它,我們通過 bucket 傳遞我們預期的單個選項對象 , key , 和 expires .

在這裡,作為演示,我們傳遞 cheatcode-tutorials bucket(在我們的教程中用於測試),一個已經存在於我們的 bucket panda.jpeg 中的文件 作為 key , 和 expires 設置為 5 (意思是,使我們返回的 URL 過期並存儲在 const signedUrl 五秒鐘後到這裡)。

我們將這個值設置得相當低,以展示當一個 URL 被訪問超過其過期時間時會發生什麼(您很可能希望根據您的用例將這個值設置得更高)。為了展示這些 URL 的工作原理,我們調用 res.send() 使用一些虛擬 HTML 響應對該路由的任何請求,顯示完整的 signedUrl 我們從亞馬遜回來,因為我們知道這是一個 .jpeg 文件——在 <img /> 中呈現該 URL 標記。

在此之下,我們添加了一個帶有 setTimeout() 的短腳本 六秒後將瀏覽器重定向到我們的signedUrl的方法。假設我們的 expires 尊重 5 秒的值,當我們訪問此 URL 時,我們希望它無法訪問:

在我們的演示中,我們可以看到,當我們加載頁面時,我們會返回我們的 URL(以及我們的熊貓圖片)。六秒後,我們重定向到完全相同的 URL(沒有對其進行更改)並發現 AWS 拋出一個錯誤,告訴我們“請求已過期”。這證實了我們簽名的 URL 的行為符合預期,並在創建後 5 秒過期。

總結

在本教程中,我們學習瞭如何使用 aws-sdk 為 S3 對像生成簽名的臨時 URL 包裹。我們學習瞭如何編寫一個包裝函數,它既可以建立與 AWS 的連接,又可以生成我們的簽​​名 URL。

為了演示我們的功能,最後,我們連接了一個 Express.js 路由,返回一些帶有圖像標籤的 HTML,呈現我們的簽名 URL,然後在幾秒鐘後重定向以驗證簽名 URL 是否正確過期。


Tutorial JavaScript 教程
  1. Express Helmet:安全的 Node.js 應用程序必備的安全帶

  2. JavaScript 數組到不帶逗號的字符串 |轉換代碼

  3. 10 個最令人驚嘆的 JS 庫,您幾乎會喜歡在項目中使用它們!

  4. 隱式傳遞對等依賴

  5. 將局部變量的值複製到 JavaScript 中的全局變量

  6. Soundmanager 2 的皮膚/主題

  7. 停止在 React Native 導入中使用相對路徑。請改用別名。

  1. 使用 Node 在 Linux 中獲取電池容量

  2. 發布你的第一個 Browserify/Node 模塊

  3. react-datepicker 入門

  4. 如何盡快獲取異步函數中的值?

  5. 使用 React Native 創建動畫文本字段

  6. 自動化 Elgato 鑰匙燈

  7. Viro-React,增強現實變得簡單

  1. 打破將 Async/Await 與 Array.forEach() 結合使用的困惑

  2. 在 Next.js 中使用 Bootstrap + 免費啟動器

  3. React 中類和函數組件的區別

  4. 項目規劃:樣板