本文介绍了如何在区块链节点中集成JSON Web Tokens (JWT)以保护网络资源。文章详细讲解了JWT的核心概念,并通过一个React应用示例展示了如何生成、验证JWT,并将其用于请求和展示区块链数据。
安全性是保护网络资源时的关键因素。保护这些资源的一种方式是将 JSON Web Tokens (JWT) 集成到你的 web 应用程序中。JWT 是一种开放标准,定义了一种在各方之间安全传输信息的简便方法。在本指南中,你将学习 JWT 的核心概念,以及如何在你的区块链节点上通过 QuickNode 启用 JWT。我们将通过创建一个 React 应用来演示这个过程,该应用将从我们的后端服务器(Express.js)获取 JWT,然后使用它来请求和显示前端的区块链数据。
依赖 | 版本 |
---|---|
node.js | ^18.16.0 |
express | ^4.19.2 |
jsonwebtoken | ^9.0.2 |
react | ^18.3.1 |
JSON Web Tokens (JWT) 的思想,也称为 JOT,是创建一种标准和安全的方式,使两个主体能够进行通信。JWT 用于无状态会话管理(没有会话 cookie),后端服务器无需与授权服务器通信,因此可以带来好处。JWT 可以通过 URL、POST 参数或 HTTP 头部内部发送。由于 JWT 的大小,它们也是高效的。JWT 由三个部分组成:头部(Header)、有效载荷(Payload)和签名(Signature)。每部分由句点(".")分隔,并且源于 JSON 对象,然后通过 BASE64 编码为文本字符串。所有组件组合在一起表示如下格式:
header.payload.signature
RFC-7519 对该标准的定义有更多详细信息。要深入了解每个组件及其如何解码 JWT,请访问 JWT.io。
这些组合的组件构成了一个 JWT。JWT 的示例可能看起来像这样:
eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2Njk0Mzg4MDAsImlhdCI6MTY0ODgzNjY3Mn0.N4aNN5cwnYcMeZHeElrYtWcLxjfR-PqqLAvZNdCTGa-5wD_O3yFIM3F4ACX7egGXMCdob7UQwytMcMoAtnpKs8wyzdOlJgWyJIg3NsIWYdjnzuLz_EqNvIPdm3TEi7m0mZbgXLOGRQ_JS1RAF2i3XKPmhQuXq_ObSryPFfsLITimXJyoi5fiyX7WkUwJELIQVAlW5M5Um1fqIK791g52lIfM9vTU_PKIEJaMaBfUJMx92M04Mt8kYBjj7Eh1rxbEWeFA545HF_QKJe3j4EKoAx6BhL6oPCmoPoZXIAYlgAX6kf-FcbFfJ76u1ppeWsC0z_tfM5eDpVxHr2GY9d0o-A
kid(密钥 ID)
在 JWT 中,“kid”(密钥 ID)头部参数标识应该用来验证 JWT 签名的密钥。这个可选的区分大小写的字符串值(例如“0123456789”)对我们来说很有用,因为我们可能在一个区块链节点端点下有多个 JWT,并允许验证者知道在有多个密钥可用时应该使用哪个公钥。有关该参数的更多信息可以在 这里 找到。
用户现在可以使用 JWT 认证方法来保护他们的区块链节点。QuickNode 支持单独的 JWT 或与我们的其他安全方法(如授权Token、推荐白名单和域名遮罩)集成。然而,请注意,如果在一个区块链节点上启用了多个身份验证服务,必须通过所有安全检查,或者用户请求将返回错误。
QuickNode 支持使用 RS256 或 ECDSA256 算法创建的密钥对生成的 JWT,并且可以在一个区块链节点上同时激活的 JWT 数量没有限制(你只需确保使用适当的密钥 ID)。现在,我们准备创建我们的 React 应用程序了!
让我们从一个简单的 JWT 认证示例开始,使用 Node.js 脚本和 cURL。这将帮助了解核心概念,然后再构建完整的 React web 应用程序。
首先,创建一个新目录并初始化它:
mkdir quicknode-jwt-demo
cd quicknode-jwt-demo
npm init -y
npm install jsonwebtoken
接下来,让我们创建密钥对。
在你的项目文件夹中创建一个名为 generateKeys.js 的文件,并输入以下代码(你可以根据需求使用 RSA256
或 ES256
版本):
// 导入的依赖
const fs = require('fs')
const { generateKeyPairSync } = require('crypto');
var jwt = require('jsonwebtoken');
// 生成 RSA 密钥
const { publicKey, privateKey } = generateKeyPairSync('rsa', {
modulusLength: 2048,
publicKeyEncoding: {
type: 'spki',
format: 'pem'
},
privateKeyEncoding: {
type: 'pkcs8',
format: 'pem',
}
});
// 将密钥对写入文件
fs.writeFileSync('private_key.pem', privateKey);
console.log('已保存 private_key.pem');
fs.writeFileSync('public_key.pem', publicKey);
console.log('已保存 public_key.pem');
// 导入的依赖
const fs = require('fs')
const { generateKeyPairSync } = require('crypto');
var jwt = require('jsonwebtoken');
// 生成 ES256 密钥
const { publicKey, privateKey } = generateKeyPairSync('ec', {
namedCurve: 'P-256',
publicKeyEncoding: {
type: 'spki',
format: 'pem'
},
privateKeyEncoding: {
type: 'pkcs8',
format: 'pem',
}
});
// 将密钥对写入文件
fs.writeFileSync('private_key.pem', privateKey);
console.log('已保存 private_key.pem');
fs.writeFileSync('public_key.pem', publicKey);
console.log('已保存 public_key.pem');
使用以下命令运行脚本:
node generateKeys.js
这将根据你的配置(例如,algorithm
、2048 位密钥长度(标准安全长度)
和 spki/pem
格式)创建一个密钥对,生成以下文件:
首先,为 Ethereum Mainnet 创建一个 QuickNode 端点。然后,导航到你节点的 Security 标签,点击 禁用 JWTs 切换以启用设置,然后点击 添加 JWT 按钮。
为你的 JWT 输入一个名称,并将先前在 public_key.pem
文件中生成的公钥粘贴到第二个字段中,标题为“公钥”。
请注意公钥的格式应与上面的图像匹配。
添加后,公钥的 名称
、ID
和 指纹
将显示在 JSON Web Token 部分。重要:ID
字段中的值应与你的脚本(即 index.js)中的 keyid
值匹配,否则你将遇到未授权错误。
接下来,我们将基于刚才创建的密钥对生成一个 JWT 。
创建另一个名为 generateJWT.js 的文件,并输入以下代码:
// generateToken.js
const jwt = require('jsonwebtoken');
const fs = require('fs');
const privateKey = fs.readFileSync('private_key.pem');
// 生成一个Token
const token = jwt.sign({}, privateKey, {
algorithm: 'RS256',
expiresIn: '2d',
keyid: '0123456789' // 你的任意 JWT ID
});
console.log(token);
运行该脚本以获取Token:
node generateJWT.js > token.txt
现在使用 curl 测试Token:
TOKEN=$(cat token.txt)
curl -X POST \
YOUR_QUICKNODE_HTTP_ENDPOINT \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $TOKEN" \
-d '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}'
请记得将
YOUR_QUICKNODE_HTTP_ENDPOINT
替换为你实际的 QuickNode 端点
如果成功,你将看到最新区块编号以十六进制值返回。这确认你 JWT 的设置正确无误。
就是这样!你刚刚学习了如何创建密钥对、生成 JWT 并使用 cURL 验证 JWT 是否有效。在下一部分,我们将展示一个类似的过程,而是构建一个使用 Express.js 的 React 应用和后端服务器。
我们的应用将使用 Express.js 作为后端,这将创建一个 REST API,该 API 将通过特定端点提供 JWT Token。我们的 React 前端将获取此 JWT,然后使用它向区块链节点请求数据。以下步骤将向你展示如何在终端窗口中创建所需的文件,但请随便使用诸如 VScode 之类的代码编辑器。
在你希望创建项目的目录中打开一个终端窗口并运行以下命令:
npx create-react-app jwt-app && cd jwt-app
一旦我们的 React 应用初始化完成,让我们安装 JWT 实现所需的依赖项:
npm i express jsonwebtoken
然后我们将创建所需的文件,一个用于我们的 Express.js 后端服务器,另一个用于我们的密钥对生成。
touch index.js && touch generateKeys.js
我们还需要配置 package.json 文件以包含代理字段。代理字段将允许在我们的 React 应用和运行在 Express.js 上的后端服务器之间进行网关连接。在你的 package.json 文件中添加以下字段:
"proxy": "http://localhost:3001"
接下来,在终端窗口中运行命令 nano generateKeys.js 以打开该文件(或使用像 VSCode 这样的代码编辑器)。然后在下面包含以下代码片段(例如,RSA256 JavaScript 或 ES256 JavaScript 示例):
// 导入的依赖
const fs = require('fs')
const { generateKeyPairSync } = require('crypto');
var jwt = require('jsonwebtoken');
// 生成 RSA 密钥
const { publicKey, privateKey } = generateKeyPairSync('rsa', {
modulusLength: 2048,
publicKeyEncoding: {
type: 'spki',
format: 'pem'
},
privateKeyEncoding: {
type: 'pkcs8',
format: 'pem',
}
});
// 将密钥对写入文件
fs.writeFileSync('private_key.pem', privateKey);
console.log('已保存 private_key.pem');
fs.writeFileSync('public_key.pem', publicKey);
console.log('已保存 public_key.pem');
// 导入的依赖
const fs = require('fs')
const { generateKeyPairSync } = require('crypto');
var jwt = require('jsonwebtoken');
// 生成 ES256 密钥
const { publicKey, privateKey } = generateKeyPairSync('ec', {
namedCurve: 'P-256',
publicKeyEncoding: {
type: 'spki',
format: 'pem'
},
privateKeyEncoding: {
type: 'pkcs8',
format: 'pem',
}
});
// 将密钥对写入文件
fs.writeFileSync('private_key.pem', privateKey);
console.log('已保存 private_key.pem');
fs.writeFileSync('public_key.pem', publicKey);
console.log('已保存 public_key.pem');
按下 CTRL + X,然后在键盘上点击 Enter 以保存文件。保存完文件后,我们将运行命令 node generateKeys.js 以创建密钥对(public_key.pem 和 private_key.pem)并将其存储在同一目录中。请注意,该文件应仅运行一次,否则每次执行该文件时都会生成新的密钥对。
接下来的步骤是创建我们的服务器代码。运行命令 nano index.js 并输入以下代码:
// 导入库
const express = require("express");
const fs = require("fs");
const jwt = require("jsonwebtoken");
// 服务器代码
const PORT = process.env.PORT || 3001;
const app = express();
// 内存存储Token
let currentToken = null;
let tokenExpiration = null;
app.get('/get_token', (req, res) => { // 用于获取 JWT 的端点
try {
// 检查当前Token是否仍然有效
if (currentToken) {
try {
const privateKey = fs.readFileSync(__dirname + "/private_key.pem");
jwt.verify(currentToken, privateKey);
if (tokenExpiration > Date.now()) {
console.log("使用现有的Token,过期时间为:", (tokenExpiration - Date.now()) / 1000, "秒");
return res.json({ token: currentToken }); // 返回现有有效的Token
}
} catch (err) {
console.log("Token过期或无效,生成新Token");
currentToken = null;
}
}
// Token过期或不存在,创建一个新的
const privateKey = fs.readFileSync(__dirname + "/private_key.pem");
currentToken = jwt.sign({}, privateKey, {
algorithm: 'RS256', // 用于创建 JWT 的算法,如有必要请更改为 ES256
expiresIn: "2d", // 设置过期为 2 天
keyid: '12345' // JSON Web Token 部分的 ID
});
// 设置过期时间
tokenExpiration = Date.now() + (2 * 24 * 60 * 60 * 1000); // 2 天的毫秒值
console.log("生成新的Token,过期时间为 2 天");
return res.json({ token: currentToken }); // 返回新创建的 JWT
} catch (error) {
console.error('Token生成错误:', error);
return res.status(500).json({ error: '生成Token失败' });
}
});
app.listen(PORT, () => {
console.log(`服务器在 ${PORT} 上监听`); // 在指定端口监听
});
警告
如果你之前使用 ES256 生成了公钥和私钥(而不是 RS256),请在 algorithm
字段中将上述值更改为 ES256
。
请记得保存该文件!现在,我们开始创建前端代码。
打开位于 src
目录中的 App.js 文件,并输入以下代码。还要记得将字符串 - "YOUR_QUICKNODE_HTTP ENDPOINT" 替换为你实际的 QuickNode 端点。如果你还没有端点,可以在这里 注册一个免费 QuickNode 账户并轻松设置端点!
import React from "react";
import "./App.css";
function App() {
const [data, setData] = React.useState(null);
React.useEffect(() => {
fetch("/get_token")
.then((res) => res.json())
.then((token) => {
fetch("YOUR_QUICKNODE_HTTP_ENDPOINT", { // 还没有?去 quicknode.com 现在就创建一个吧!
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
'Authorization': `Bearer ${token.token}`
},
body: JSON.stringify({ method: "eth_blockNumber", params: [], id: 1, jsonrpc: "2.0" })
}).then(res => res.json()).then(content => {
console.log(content)
setData(parseInt(content.result))
});
});
})
return (
<div className="App">
<header className="App-header">
<p>{!data ? "加载中..." : data}</p>
</header>
</div>
);
}
export default App;
我们快完成了!在我们可以启动应用程序之前,我们需要将我们的公钥对添加到我们的区块链节点。接下来让我们在 QuickNode 上设置 JWT 认证。
首先,为 Ethereum Mainnet 创建一个 QuickNode 端点。然后,导航到你节点的 Security 标签,点击 禁用 JWTs 切换以启用设置,然后点击 添加 JWT 按钮。
为你的 JWT 输入一个名称,并将先前在 public_key.pem
文件中生成的公钥粘贴到第二个字段中,标题为“公钥”。
请注意公钥的格式应与上面的图像匹配。
添加后,公钥的 名称
、ID
和 指纹
将显示在 JSON Web Token 部分。重要:ID
字段中的值应与你的脚本(即 index.js)中的 keyid
值匹配,否则你将遇到未授权错误。
在启动 Express API 和 React 应用之前,让我们总结一下它们将如何协作。
/get_token
端点,该端点一旦被调用,将读取本地私钥,创建并签署 JWT 有效载荷,然后返回签名的 JWT。/get_token
端点并获取签名的 JWT(即 bearer Token)。eth_blockNumber
RPC 方法获取最新区块号,并将签名的 JWT 包含在请求头中。现在,我们需要打开两个终端窗口,以便可以同时运行后端服务器和 React 应用。
要启动后端服务器,在一个终端窗口中运行命令 node index.js。如果服务器正在运行,你应该会看到以下输出:“服务器在 3001 上监听”
然后,在另一个终端窗口中运行命令 npm start 启动 React 应用。浏览器窗口应该会自动打开到应用运行的端口,你应该会看到类似以下的结果:
如果你想进一步深入了解,请查看这个精彩的 GitHub 存储库,它展示了如何在 QuickNode 上实现与 JWT 认证的 Cloudflare workers。
下面是一些用户在使用 QuickNode 的 JWT 时可能会有的常见问题的答案。如果你对该功能或其他功能有任何进一步的问题,请随时与我们联系!
问:支持在哪些链上使用 JWT?
答: JWT 在所有链上均受支持。
问: 在过期前,我如何丢弃 JWT?
答: 如果你的 JWT 可能已暴露,并且你希望阻止其被使用,你可以从节点的安全选项卡中移除公钥。
问: 我可以将多个 JWT 添加到一个区块链节点吗?
答: 在一个区块链节点上同时激活的 JWT 数量没有限制。如果多个 JWT 正在激活,则确保在 keyid
字段中使用适当的 ID 值,对应于你在签署 JWT 时使用的公钥/私钥。有关更多信息,请参见 这里。
问: QuickNode 支持哪种加密方式?
答: 目前只支持 RSA 256 (RS256) 和 ECDSA 256 (ES256),如 RFC7518 表 3.1 中所述。
问: 如何解决 401 错误?
答: 你可能已经在你的区块链节点上启用了其他形式的安全方法。请检查以确保你的请求满足所有安全检查。
问: 我应该选择什么过期日期?
答: 你可以设置所希望的任何将来的过期日期。通常,过期时间越短,JWT 的安全性越高。
恭喜你完成本指南!我们学习了 JWT 及如何通过 QuickNode 在你的区块链节点上启用它。请订阅我们的 通讯,以获取更多关于以太坊的文章和指南。如果你有任何反馈,请随时通过 Twitter 与我们联系。你也可以在我们的 Discord 社区服务器上与我们聊天,那里聚集了一些你可能遇到的最酷的开发人员 😃
让我们知道 如果你有任何反馈或对新主题的请求。我们很乐意听到你的声音。
- 原文链接: quicknode.com/guides/qui...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!