【n8n教程】:JWT节点,实现安全的工作流认证

什么是JWT?你真的需要了解它!

如果你正在使用n8n构建工作流,并且需要保护你的API或Webhook,JWT(JSON Web Token)就是你的好朋友。JWT是一种轻量级的、自包含的安全认证标准,被全球数百万应用使用,包括Google、Microsoft和Auth0等大型平台。

简单来说,JWT就像一张带有加密签名的身份证。当你的应用向另一个服务请求数据时,可以通过JWT来证明"我就是谁",而接收方可以通过验证签名来确认这个身份没有被篡改。这就是为什么JWT在现代API认证中如此重要。

JWT的三个核心部分

JWT看起来像这样:xxxxx.yyyyy.zzzzz

它由三个部分组成,用点(.)分隔开:

1. Header(头部)

包含两个关键信息:


    
    
    
  {
  "alg"
: "HS256",
  "typ"
: "JWT"
}

2. Payload(载荷)

这是JWT的主要内容,包含Claims(声明)——关于用户或数据的陈述。例如:


    
    
    
  {
  "sub"
: "user123",
  "name"
: "张三",
  "iat"
: 1516239022,
  "exp"
: 1516242622
}

常见的Claims包括:

3. Signature(签名)

这是最关键的部分。签名通过以下方式生成:

  1. 1. 对Header进行Base64编码
  2. 2. 对Payload进行Base64编码
  3. 3. 使用密钥和算法对它们进行签名

签名确保JWT没有被篡改过。如果有人改变了Payload中的任何数据,签名就会失效。

n8n JWT节点的三个核心操作

n8n提供了一个专门的JWT节点,让你可以轻松处理JWT。这个节点有三个主要操作:

操作1️⃣:Sign(签名) - 创建JWT

这是生成一个新的JWT令牌。当你想要为某个用户或请求创建一个令牌时,就需要使用这个操作。

使用场景:用户登录后,服务器为其生成一个JWT,用于后续请求的认证。

参数配置

操作2️⃣:Decode(解码) - 读取JWT内容

解码操作用于查看JWT中的内容,而不验证签名。这在调试或检查令牌结构时很有用。

使用场景:你收到一个JWT,想要看看里面有什么数据。

参数配置

⚠️ 重要提示:解码不会验证令牌是否真实有效,任何人都可以解码JWT并看到其内容。这就是为什么你永远不应该在JWT中放置敏感信息如密码。

操作3️⃣:Verify(验证) - 确保JWT真实有效

这是最重要的操作。验证操作检查JWT的签名是否有效,以及令牌是否过期。

使用场景:当你收到来自客户端的请求,并且请求中包含一个JWT时,你需要验证这个JWT是否真的由你发放的,以及是否已过期。

参数配置

快速开始:5分钟配置JWT认证

第一步:创建JWT凭证

  1. 1. 在n8n中,点击左下角的 Personal → Credentials
  2. 2. 点击 Create New Credential
  3. 3. 选择 JWT 作为凭证类型
  4. 4. 配置以下信息:
    • Key Type:选择 Passphrase(用于HS256对称加密)或 PEM Key(用于RS256非对称加密)
    • Secret:输入你的密钥(对称加密下是一个长随机字符串)
    • Algorithm:选择 HS256(推荐用于初学者)

💡 生成安全密钥的方法


    
    
    
  # 使用以下命令在终端生成一个强密钥
openssl rand -hex 32

第二步:在工作流中添加JWT节点

  1. 1. 创建一个新工作流
  2. 2. 从节点面板中搜索并添加 JWT 节点
  3. 3. 配置节点操作

第三步:根据需要选择操作

根据你的场景选择Sign、Decode或Verify操作,我们在下面的实战案例中会详细演示。

实战案例:用户认证工作流

下面是一个完整的、可直接导入n8n的工作流示例,演示了JWT的完整使用流程:

工作流说明

  1. 1. 用户提交登录请求
  2. 2. n8n 签名一个包含用户信息的JWT
  3. 3. 客户端在后续请求中发送这个JWT
  4. 4. n8n 验证JWT的真实性和有效性
  5. 5. 如果验证成功,允许访问资源

完整工作流JSON代码


    
    
    
  {
  "name"
: "JWT完整示例工作流",
  "description"
: "演示JWT的签名、解码和验证完整流程",
  "nodes"
: [
    {

      "parameters"
: {},
      "id"
: "1",
      "name"
: "开始工作流",
      "type"
: "n8n-nodes-base.manualTrigger",
      "typeVersion"
: 1,
      "position"
: [0, 0]
    }
,
    {

      "parameters"
: {
        "operation"
: "sign",
        "useJson"
: false,
        "claimsObject"
: {
          "iss"
: "n8n_demo",
          "sub"
: "user123",
          "aud"
: "n8n_app",
          "exp"
: 3600
        }
,
        "options"
: {}
      }
,
      "id"
: "2",
      "name"
: "JWT签名",
      "type"
: "n8n-nodes-base.jwt",
      "typeVersion"
: 1,
      "position"
: [200, 0],
      "credentials"
: {
        "jwtAuth"
: "jwt_secret_key"
      }

    }
,
    {

      "parameters"
: {
        "operation"
: "decode",
        "token"
: "={{ $json.token }}",
        "options"
: {
          "returnAdditionalInfo"
: true
        }

      }
,
      "id"
: "3",
      "name"
: "JWT解码",
      "type"
: "n8n-nodes-base.jwt",
      "typeVersion"
: 1,
      "position"
: [400, 0]
    }
,
    {

      "parameters"
: {
        "operation"
: "verify",
        "token"
: "={{ $json.token }}",
        "options"
: {
          "returnAdditionalInfo"
: true,
          "ignoreExpiration"
: false,
          "ignoreNotBeforeClaim"
: false,
          "clockTolerance"
: 0
        }

      }
,
      "id"
: "4",
      "name"
: "JWT验证",
      "type"
: "n8n-nodes-base.jwt",
      "typeVersion"
: 1,
      "position"
: [600, 0],
      "credentials"
: {
        "jwtAuth"
: "jwt_secret_key"
      }

    }
,
    {

      "parameters"
: {
        "mode"
: "each",
        "expression"
: "={{ $json.payload }}",
        "options"
: {}
      }
,
      "id"
: "5",
      "name"
: "返回结果",
      "type"
: "n8n-nodes-base.respondToWebhook",
      "typeVersion"
: 1,
      "position"
: [800, 0]
    }

  ]
,
  "connections"
: {
    "开始工作流"
: {
      "main"
: [
        [

          {

            "node"
: "JWT签名",
            "type"
: "main",
            "index"
: 0
          }

        ]

      ]

    }
,
    "JWT签名"
: {
      "main"
: [
        [

          {

            "node"
: "JWT解码",
            "type"
: "main",
            "index"
: 0
          }

        ]

      ]

    }
,
    "JWT解码"
: {
      "main"
: [
        [

          {

            "node"
: "JWT验证",
            "type"
: "main",
            "index"
: 0
          }

        ]

      ]

    }
,
    "JWT验证"
: {
      "main"
: [
        [

          {

            "node"
: "返回结果",
            "type"
: "main",
            "index"
: 0
          }

        ]

      ]

    }

  }
,
  "active"
: true,
  "settings"
: {
    "executionOrder"
: "v1"
  }

}

导入这个工作流

  1. 1. 复制上面的JSON代码
  2. 2. 在n8n中,点击 Add workflow → Import from code
  3. 3. 粘贴JSON代码,点击 Import
  4. 4. 创建JWT凭证并连接到相应节点
  5. 5. 点击 Test 运行工作流

JWT最佳安全实践

✅ 做这些事:

  1. 1. 使用强密钥:至少32个字符的随机密钥
  2. 2. 设置合理的过期时间exp claim应该是较短的时间(如1小时)
  3. 3. 验证所有声明:不要只验证签名,还要检查 aud(受众)
  4. 4. 使用HTTPS:始终通过加密连接传输JWT
  5. 5. 在Header中发送:使用 Authorization: Bearer <token> 格式

❌ 不要做这些事:

  1. 1. 不要在JWT中存储密码或敏感数据:JWT是可读的,任何人都可以解码
  2. 2. 不要使用弱密钥:避免使用预测性的或简单的密钥
  3. 3. 不要忽略过期检查:过期的令牌应该被拒绝
  4. 4. 不要在URL中传递JWT:容易被记录和拦截
  5. 5. 不要相信未验证的JWT:始终在接收端验证

常见问题解答

Q: HS256和RS256有什么区别?
A: HS256是对称加密(一个密钥签名和验证),RS256是非对称加密(一个私钥签名,一个公钥验证)。HS256更简单,RS256更安全(公钥可以安全分享)。

Q: JWT过期后能延长吗?
A: 不能直接延长。通常的做法是使用 Refresh Token 机制:发放一个长期的Refresh Token,当Access Token过期时用它来获取新的Access Token。

Q: 如何在Webhook中使用JWT认证?
A: 在n8n Webhook节点中,选择 AuthenticationJWT,然后选择你的JWT凭证。n8n会自动验证传入请求中的JWT。

Q: 可以在一个工作流中使用多个不同的JWT吗?
A: 可以。创建多个JWT凭证,在不同的JWT节点中使用不同的凭证。

总结

现在你已经掌握了n8n JWT节点的核心概念和使用方法!这些技能将帮助你:


官方文档[1]
n8n系列教程[2]

引用链接

[1] 官方文档: https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-base.jwt/
[2] n8n系列教程: https://www.undsky.com/blog/?category=n8n%E6%95%99%E7%A8%8B#