javascript
基于 Token 的身份验证:JSON Web Token
——
文章先介紹了一下傳統身份驗證與基于 JWT 身份驗證的方法,再理解一下 JWT 的 Token 的組成部分(頭部,數據,簽名),
最后我們會在一個 Node.js 項目上實施簽發與驗證 JWT 的功能。練習的視頻版本可以參考《JWT:JSON Web Token》這個免費的課程,項目代碼在 Github 上可以找到。
傳統身份驗證的方法
HTTP 是一種沒有狀態的協議,也就是它并不知道是誰是訪問應用。這里我們把用戶看成是客戶端,客戶端使用用戶名還有密碼通過了身份驗證,不過下回這個客戶端再發送請求時候,還得再驗證一下。
解決的方法就是,當用戶請求登錄的時候,如果沒有問題,我們在服務端生成一條記錄,這個記錄里可以說明一下登錄的用戶是誰,然后把這條記錄的 ID 號發送給客戶端,客戶端收到以后把這個 ID 號存儲在 Cookie 里,下次這個用戶再向服務端發送請求的時候,可以帶著這個 Cookie ,這樣服務端會驗證一個這個 Cookie 里的信息,看看能不能在服務端這里找到對應的記錄,如果可以,說明用戶已經通過了身份驗證,就把用戶請求的數據返回給客戶端。
上面說的就是 Session,我們需要在服務端存儲為登錄的用戶生成的 Session ,這些 Session 可能會存儲在內存,磁盤,或者數據庫里。我們可能需要在服務端定期的去清理過期的 Session 。
基于 Token 的身份驗證方法
使用基于 Token 的身份驗證方法,在服務端不需要存儲用戶的登錄記錄。大概的流程是這樣的:
JWT
實施 Token 驗證的方法挺多的,還有一些標準方法,比如 JWT,讀作:jot ,表示:JSON Web Tokens 。JWT 標準的 Token 有三個部分:
- header(頭部)
- payload(數據)
- signature(簽名)
中間用點分隔開,并且都會使用 Base64 編碼,所以真正的 Token 看起來像這樣:
eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJuaW5naGFvLm5ldCIsImV4cCI6IjE0Mzg5NTU0NDUiLCJuYW1lIjoid2FuZ2hhbyIsImFkbWluIjp0cnVlfQ.SwyHTEx_RQppr97g4J5lKXtabJecpejuef8AqKYMAJcHeader
每個 JWT token 里面都有一個 header,也就是頭部數據。里面包含了使用的算法,這個 JWT 是不是帶簽名的或者加密的。主要就是說明一下怎么處理這個 JWT token 。
頭部里包含的東西可能會根據 JWT 的類型有所變化,比如一個加密的 JWT 里面要包含使用的加密的算法。唯一在頭部里面要包含的是 alg 這個屬性,如果是加密的 JWT,這個屬性的值就是使用的簽名或者解密用的算法。如果是未加密的 JWT,這個屬性的值要設置成 none。
示例:
{"typ": "JWT","alg": "HS256" }意思是這個 JWT 用的算法是 HS256。上面的內容得用 base64url?的形式編碼一下,所以就變成這樣:
eyJhbGciOiJIUzI1NiJ9Payload
Payload 里面是 Token 的具體內容,這些內容里面有一些是標準字段,你也可以添加其它需要的內容。下面是標準字段:
- iss:Issuer,發行者
- sub:Subject,主題
- aud:Audience,觀眾
- exp:Expiration time,過期時間
- nbf:Not before
- iat:Issued at,發行時間
- jti:JWT ID
比如下面這個 Payload ,用到了 iss 發行人,還有 exp 過期時間這兩個標準字段。另外還有兩個自定義的字段,一個是 name ,還有一個是 admin 。
{"iss": "ninghao.net","exp": "1438955445","name": "wanghao","admin": true}使用 base64url 編碼以后就變成了這個樣子:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9Signature
JWT 的最后一部分是 Signature ,這部分內容有三個部分,先是用 Base64 編碼的 header.payload ,再用加密算法加密一下,加密的時候要放進去一個 Secret ,這個相當于是一個密碼,這個密碼秘密地存儲在服務端。
- header
- payload
- secret
- const encodedString = base64UrlEncode(header) + "." + base64UrlEncode(payload); HMACSHA256(encodedString, 'secret');
處理完成以后看起來像這樣:
SwyHTEx_RQppr97g4J5lKXtabJecpejuef8AqKYMAJc最后這個在服務端生成并且要發送給客戶端的 Token 看起來像這樣:
eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJuaW5naGFvLm5ldCIsImV4cCI6IjE0Mzg5NTU0NDUiLCJuYW1lIjoid2FuZ2hhbyIsImFkbWluIjp0cnVlfQ.SwyHTEx_RQppr97g4J5lKXtabJecpejuef8AqKYMAJc客戶端收到這個 Token 以后把它存儲下來,下回向服務端發送請求的時候就帶著這個 Token 。服務端收到這個 Token ,然后進行驗證,通過以后就會返回給客戶端想要的資源。
簽發與驗證 JWT
在應用里實施使用基于 JWT 這種 Token 的身份驗證方法,你可以先去找一個簽發與驗證 JWT 的功能包。無論你的后端應用使用的是什么樣的程序語言,系統,或者框架,你應該都可以找到提供類似功能的包。
下面我們在一個 Node.js 項目里,用最簡單的方式來演示一下簽發還有驗證 JWT 的方法。練習有個視頻版本,你可以參考《?JWT:JSON Web Token?》這個免費的視頻課程。
項目代碼:https://github.com/ninghao/jwt-demo
準備項目
準備一個簡單的 Node.js 項目:
cd ~/desktop mkdir jwt-demo cd jwt-demo npm init -y安裝簽發與驗證 JWT 的功能包,我用的叫 jsonwebtoken,在項目里安裝一下這個包:
npm install jsonwebtoken --save簽發 JWT
在項目里隨便添加一個 .js 文件,比如 index.js,在文件里添加下面這些代碼:
const jwt = require('jsonwebtoken')// Token 數據const payload = {name: 'wanghao',admin: true}// 密鑰const secret = 'ILOVENINGHAO'// 簽發 Tokenconst token = jwt.sign(payload, secret, { expiresIn: '1day' })// 輸出簽發的 Tokenconsole.log(token)非常簡單,就是用了剛剛為項目安裝的 jsonwebtoken 里面提供的 jwt.sign 功能,去簽發一個 token。這個 sign 方法需要三個參數:
在命令行下面,用 node 命令,執行一下項目里的 index.js 這個文件(node index.js),會輸出應用簽發的 token:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoid2FuZ2hhbyIsImFkbWluIjp0cnVlLCJpYXQiOjE1MjkwMzM5MDYsImV4cCI6MTUyOTEyMDMwNn0.DctA2QlUCrM6wLWkIO78wBVN0NLpjoIq4T5B_2WJ-PU上面的 Token 內容并沒有加密,所以如果用一些 JWT 解碼功能,可以看到 Token 里面包含的內容,內容由三個部分組成,像這樣:
// header{"alg": "HS256", "typ": "JWT"}// payload{"admin": true, "iat": 1529033906, "name": "wanghao", "exp": 1529120306}// signatureDctA2QlUCrM6wLWkIO78wBVN0NLpjoIq4T5B_2WJ-PU假設用戶通過了某種身份驗證,你就可以使用上面的簽發 Token 的功能為用戶簽發一個 Token。一般在客戶端那里會把它保存在 Cookie 或 LocalStorage 里面。
用戶下次向我們的應用請求受保護的資源的時候,可以在請求里帶著我們給它簽發的這個 Token,后端應用收到請求,檢查簽名,如果驗證通過確定這個 Token 是我們自己簽發的,那就可以為用戶響應回他需要的資源。
驗證 JWT
驗證 JWT 的用效性,確定一下用戶的 JWT 是我們自己簽發的,首先要得到用戶的這個 JWT Token,然后用 jwt.verify 這個方法去做一下驗證。這個方法是 Node.js 的 jsonwebtoken 這個包里提供的,在其它的應用框架或者系統里,你可能會找到類似的方法來驗證 JWT。
打開項目的 index.js 文件,里面添加幾行代碼:
<// 驗證 Tokenjwt.verify(token, 'bad secret', (error, decoded) => {if (error) {console.log(error.message)return}console.log(decoded)})把要驗證的 Token 數據,還有簽發這個 Token 的時候用的那個密鑰告訴 verify 這個方法,在一個回調里面有兩個參數,error 表示錯誤,decoded 是解碼之后的 Token 數據。
執行:
node ~/desktop/jwt-demo/index.js輸出:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoid2FuZ2hhbyIsImFkbWluIjp0cnVlLCJpYXQiOjE1MjkwMzQ3MzMsImV4cCI6MTUyOTEyMTEzM30.swXojmu7VimFu3BoIgAxxpmm2J05dvD0HT3yu10vuqUinvalid signature注意輸出了一個 invalid signature ,表示 Token 里的簽名不對,這是因為我們組長 verify 方法提供的密鑰并不是簽發 Token 的時候用的那個密鑰。這樣修改一下:
jwt.verify(token, secret, (error, decoded) => { ...再次運行,會輸出類似的數據:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoid2FuZ2hhbyIsImFkbWluIjp0cnVlLCJpYXQiOjE1MjkwMzUzODYsImV4cCI6MTUyOTEyMTc4Nn0.mkNrt4TfcfmP22xd3C_GQn8qnUmlB39dKT9SpIBTBGI{ name: 'wanghao', admin: true, iat: 1529035386, exp: 1529121786 }RS256 算法
默認簽發還有驗證 Token 的時候用的是?HS256 算法,這種算法需要一個密鑰(密碼)。我們還可以使用 RS256 算法簽發與驗證 JWT。這種方法可以讓我們分離開簽發與驗證,簽發時需要用一個密鑰,驗證時使用公鑰,也就是有公鑰的地方只能做驗證,但不能簽發 JWT。
在項目下面創建一個新的目錄,里面可以存儲即將生成的密鑰與公鑰文件。
cd ~/desktop/jwt-demo mkdir config cd config密鑰
先生成一個密鑰文件:
ssh-keygen -t rsa -b 2048 -f private.key公鑰
基于上面生成的密鑰,再去創建一個對應的公鑰:
openssl rsa -in private.key -pubout -outform PEM -out public.key簽發 JWT(RS256 算法)
用 RS256 算法簽發 JWT 的時候,需要從文件系統上讀取創建的密鑰文件里的內容。
const fs = require('fs')// 獲取簽發 JWT 時需要用的密鑰const privateKey = fs.readFileSync('./config/private.key')簽發仍然使用 jwt.sign 方法,只不過在選項參數里特別說明一下使用的算法是 RS256:
// 簽發 Tokenconst tokenRS256 = jwt.sign(payload, privateKey, { algorithm: 'RS256' })// 輸出簽發的 Tokenconsole.log('RS256 算法:', tokenRS256)驗證 JWT(RS256 算法)
驗證使用 RS256 算法簽發的 JWT,需要在文件系統上讀取公鑰文件里的內容。然后用 jwt 的 verify 方法去做驗證。
// 獲取驗證 JWT 時需要用的公鑰const publicKey = fs.readFileSync('./config/public.key')// 驗證 Tokenjwt.verify(tokenRS256, publicKey, (error, decoded) => {if (error) {console.log(error.message)return}console.log(decoded)})文章轉載于: https://ninghao.net/blog/2834
基于 Token 的身份驗證:JSON Web Token
總結
以上是生活随笔為你收集整理的基于 Token 的身份验证:JSON Web Token的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 喵喵折 淘宝
- 下一篇: JS复制内容到剪贴板