首頁 > web前端 > js教程 > 主體

淺析nodejs中怎麼使用JWT?

青灯夜游
發布: 2023-01-04 21:00:18
轉載
2392 人瀏覽過

這篇文章帶大家了解JWT,介紹一下JWT在node中的應用,以及JWT的優缺點,希望對大家有幫助!

淺析nodejs中怎麼使用JWT?

什麼是JWT

JWT也就是JSON Web Token的縮寫,也就是為了在網路應用環境中一種認證解決方案,在傳統的認證機制中,無非是一下幾個步驟:

1. 用户将账号密码发送到服务器;

2. 服务器通过验证账号密码后,会在当前session中保存一些用户相关的信息,用户角色或者过期时间等等;

3. 服务器给用户一个session_id, 写入用户的Cookie或者客户端自行保存在本地;

4. 用户每次请求服务,都需要带上这个session_id,或许会通过Cookie,或者其他的方式;

5. 服务器接收到后,回去数据库查询当前的session_id,校验该用户是否有权限;
登入後複製

這種模式有一種優勢在於,伺服器隨時可以終止使用者的權限,可以去資料庫修改或刪除目前使用者的session資訊。但是也有一點不好的,就是如果是伺服器集群的話,所有的機器就需要共享這些session信息,確保每台伺服器都能夠獲取到相同的session存儲信息。雖然可以解決這些問題,但是工程量龐大。

JWT方案的優勢呢,就是不保存這些信息,token資料保存在客戶端,每次接受請求時,只需要校驗就好。 【相關教學推薦:nodejs影片教學程式設計教學

#JWT的原理

簡單說一下JWT的原理,其實就是客戶端發送請求認證的時候,伺服器在認證用戶之後,會產生一個JSON對象,大概包括「你是誰,你是乾嘛的等等,到期時間」這些信息,重要的是一定要有到期時間;大致格式為:

{
    username: "贼烦字符串er",
    role: "世代码农",
    endTime: "2022年5月20日"
}
登入後複製

但是不會用這麼膚淺的方式傳給你,它會通過制定的簽名算法和你提交的payload的一些信息進行可逆的簽名演算法進行簽名後傳輸,大致的格式我用一張圖片表示:

淺析nodejs中怎麼使用JWT?

#由圖片可以看出,返回的信息大致分為三部分,左側為簽名之後的結果,也就是返回給客戶端的結果,右側也是就Decoded的源碼了,三部分由「點」隔開,分別由紅、紫、青三種顏色一一對應:

  • 第一個紅色部分是Header,Header中主要是指定了的方式,圖中的簽名演算法(預設HS256)就是帶有SHA-256 的HMAC是對稱演算法, 雙方之間隻共用一個金鑰,typ欄位標識為JWT類型;

  • #第二個紫色部分payload,就是一個JSON對象,也就是實際要傳輸的數據,官方有七個欄位可以使用:

    • iss (issuer):簽發人
    • exp (expiration time):過期時間
    • sub ( subject):主題
    • aud (audience):受眾
    • nbf (Not Before):生效時間
    • iat (Issued At):簽發時間
    • jti (JWT ID):編號

除了這些字段,你還可以搞一些自訂的字段,由於JWT預設是不加密的,所以在使用的時候盡量注意不要使用一些敏感資料。

  • 第三部分就是Signature簽名,這一部分,是由你自己指定且只有伺服器存在的秘鑰,然後使用頭部指定的演算法通過下面的簽名方法進行簽名。

JWT的簡單使用

下面我們來感受一下具體的使用:

第一步:我們需要搭建一個node的專案;透過npm init -y初始化一個專案;之後我們需要安裝依賴,分別按狀expressjsonwebtokennodemon三個依賴:

$ npm i express jsonwebtoken nodemon
登入後複製

之後在package.json中的scripts欄位中加入nodemon app.js指令:

"scripts": {
    "start": "nodemon app.js"
},
登入後複製

第二步:初始化一下node應用,在根目錄下建立app.js檔案;

// app.js

const express = require("express");
const app = express();

app.use(express.json());

app.listen(3000, () => {
  console.log(3000 + " listening..."); // 监听3000端口
});
登入後複製

第三步:引入jsonwebtoken依賴,並且創建接口和伺服器的私鑰;

// app.js

//...
const jwt = require("jsonwebtoken");

const jwtKey = "~!@#$%^&*()+,";
// ...
登入後複製

這裡面的jwtKey#是我們自訂保存僅限保存在伺服器中的私鑰,之後我們開始寫一個/login 接口,用來登錄,並且建立本地的模擬資料庫用來校驗,並透過jwt.sign方法進行校驗簽章:

// app.js
const database = {
  username: "username",
  password: "password",
};

app.post("/login", (req, res) => {
  const { username, password } = req.body;
  if (username === database.username && password === database.password) {
    jwt.sign(
      {
        username,
      },
      jwtKey,
      {
        expiresIn: "30S",
      },
      (_, token) => {
        res.json({
          username,
          message: "登陆成功",
          token,
        });
      }
    );
  }
});
登入後複製

上面程式碼中我們建立了database 變數來模擬創建了本地的帳號密碼資料庫,用來校驗登陸;接下來建立了一個/loginpost接口,在校驗帳號密碼完全匹配之後,我們透過jsonwebtoken套件導入的jwt物件下的人sign方法進行簽名,這個方法有三種介面簽名:

export function sign(
    payload: string | Buffer | object,
    secretOrPrivateKey: Secret,
    options?: SignOptions,
): string;

export function sign(
    payload: string | Buffer | object,
    secretOrPrivateKey: Secret,
    callback: SignCallback,
): void;

export function sign(
    payload: string | Buffer | object,
    secretOrPrivateKey: Secret,
    options: SignOptions,
    callback: SignCallback,
): void;
登入後複製

这里用到了函数重载的方式实现接口,我们这里将实现最后一个接口签名,第一个参数可以是一个自定义的对象类型,也可以是一个Buffer类型,还可以直接是一个string类型,我们的源码使用了object类型,自定义了一些字段,因为jwt在进行签名是也会对这些数据一并进行签名,但是值得注意的是,这里尽量不要使用敏感数据,因为JWT默认是不加密的,它的核心就是签名,保证数据未被篡改,而检查签名的过程就叫做验证

当然你也可以对原始Token进行加密后传输;

第二个参数:是我们保存在服务器用来签名的秘钥,通常在客户端-服务端模式中,JWS 使用 JWA 提供的 HS256 算法加上一个密钥即可,这种方式严格依赖密钥,但在分布式场景,可能多个服务都需要验证JWT,若要在每个服务里面都保存密钥,那么安全性将会大打折扣,要知道,密钥一旦泄露,任何人都可以随意伪造JWT。

第三个参数:是签名的选项SignOptions,接口的签名:

export interface SignOptions {
    algorithm?: Algorithm | undefined;
    keyid?: string | undefined;
    expiresIn?: string | number | undefined;
    /** expressed in seconds or a string describing a time span [zeit/ms](https://github.com/zeit/ms.js).  Eg: 60, "2 days", "10h", "7d" */
    notBefore?: string | number | undefined;
    audience?: string | string[] | undefined;
    subject?: string | undefined;
    issuer?: string | undefined;
    jwtid?: string | undefined;
    mutatePayload?: boolean | undefined;
    noTimestamp?: boolean | undefined;
    header?: JwtHeader | undefined;
    encoding?: string | undefined;
}
登入後複製

这里我们用的是expiresIn字段,指定了时效时间,使用方法参考这个文档;

第四个参数是一个回调,回调的第二个参数就是我们通过签名生成的token,最后将这个token返回给前端,以便存储到前端本地每次请求是带上到服务端进行验证。

接下来,我们来验证一下这个接口: 我是在vscode安装的REST Client插件,之后在根目录创建一个request.http的文件,文件内写上请求的信息:

POST http://localhost:3000/login
content-type: application/json

{
  "username": "username",
  "password": "password"
}
登入後複製

之后在命令行执行npm run start命令启动服务,之后在requset.http文件上方点击Send Request按钮,发送请求:

淺析nodejs中怎麼使用JWT?

请求成功后,会看到这样的响应报文:

淺析nodejs中怎麼使用JWT?

token字段就是我们JWT生成的token;

下面来验证一下这个token是否有效,我们在写一个登录过后的接口:

app.get("/afterlogin", (req, res) => {
  const { headers } = req;
  
  const token = headers["authorization"].split(" ")[1];
  // 将token放在header的authorization字段中
  jwt.verify(token, jwtKey, (err, payload) => {
    if (err) return res.sendStatus(403);
    res.json({ message: "认证成功", payload });
  });
});
登入後複製

这段代码中,通过获取请求头中的authorization字段中的token进行获取之前通过JWT生成的token。 之后通过调用jwt.verify校验方法校验这个token是否有效,这个方法分别有三个参数:

// 有四个接口签名,可以自行查文档

export function verify(
    token: string,
    // 需要检验的token
    secretOrPublicKey: Secret | GetPublicKeyOrSecret,
    // 定义在服务器的签名秘钥
    callback?: VerifyCallback<JwtPayload | string>,
    // 获取校验信息结果的回调
): void;
登入後複製

接下来我们把刚才响应的token复制到请求头中:

###
GET http://localhost:3000/afterlogin
authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6InVzZXJuYW1lIiwiaWF0IjoxNjUyNzg5NzA3LCJleHAiOjE2NTI3ODk3Mzd9.s9fk3YLhxTUcpUgCfIK4xQN58Hk_XEP5y9GM9A8jBbY
登入後複製

前面的Bearer认证, 是http协议中的标准认证方式

同样点击Send Request当看到下面图片的响应,就意味着响应成功:

淺析nodejs中怎麼使用JWT?

其实以上就是JWT的一些简单的用法,接下来再说一下JWT本身存在的优缺点.

JWT的不足

  • JWT占用的存储空间其实并不小,如果我们需要签名做过多的信息,那么token很可能会超出cookie的长度限制,例如对比一下这两张图片:

淺析nodejs中怎麼使用JWT?

很明显,随着payload的信息量增大,token的长度也会增加;

  • 安全性,其实如果token的占用空间过大,Cookie最大存储空间只有4kb前端可以存储在localStorage之类的本地存储,但是会带来一个问题,如果不是放在cookie的话,安全性就会大打折扣,就会有通过js脚本获取到的风险,就意味着任何hacker都可以拿着它做任何事情;

  • 不灵活的时效性,其实JWT的某方面意义在于用户token不需要持久化存储,而是采用服务器校验的方式对token进行有效校验,刚才看到了,签名也是把到期时间一并签名的,如果改变到期时间token就会被篡改,由于没有存储和手动更改时效的方法,所以很难立刻将这个token删掉,如果用户重复登陆两次,生成两个token,那么原则上两个token都是有效的;

总结

以上主要讲了几点:

  • JWT的原理,主要是透過伺服器的私鑰對JSON的簽章產生的token進行會話;

  • ##也介紹了JWT內部數據的組成,是由Header用來指定簽名演算法和類型的,payload來傳輸JSON數據,Signature來對數據進行簽名演算法,放置篡改;

  • ##具體介紹了一下如何透過nodejs使用JWT,透過
  • sign

    方法進行資料簽名,verify方法進行簽名驗證;

  • 也介紹了一些JWT的不足:
    • 一個是儲存空間隨著簽章資料量的增加而增加;
    • ##再有就是安全性,如果由於儲存空間過大將無法儲存在安全等級相對較高的
    • Cookie
    • 中,導致腳本可以隨意取得;

      再有就是時效性,無法靈活的控制
    • token
    • 的時效性;

  • #這個是上面的nodejs
demo原始碼
,可供參考;

https://github.com/wangzi6224/jwt-usage-nodejs

更多node相關知識,請造訪:

nodejs 教學

以上是淺析nodejs中怎麼使用JWT?的詳細內容。更多資訊請關注PHP中文網其他相關文章!

相關標籤:
來源:juejin.cn
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板
關於我們 免責聲明 Sitemap
PHP中文網:公益線上PHP培訓,幫助PHP學習者快速成長!