1. 程式人生 > 其它 >實現JWT鑑權機制

實現JWT鑑權機制

一、是什麼

JWT(JSON Web Token),本質就是一個字串書寫規範,如下圖,作用是用來在使用者和伺服器之間傳遞安全可靠的資訊

在目前前後端分離的開發過程中,使用token鑑權機制用於身份驗證是最常見的方案,流程如下:

  • 伺服器當驗證使用者賬號和密碼正確的時候,給使用者頒發一個令牌,這個令牌作為後續使用者訪問一些介面的憑證
  • 後續訪問會根據這個令牌判斷使用者時候有許可權進行訪問

Token,分成了三部分,頭部(Header)、載荷(Payload)、簽名(Signature),並以.進行拼接。其中頭部和載荷都是以JSON格式存放資料,只是進行了編碼

header

每個JWT都會帶有頭部資訊,這裡主要宣告使用的演算法。宣告演算法的欄位名為alg

,同時還有一個typ的欄位,預設JWT即可。以下示例中演算法為HS256

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

因為JWT是字串,所以我們還需要對以上內容進行Base64編碼,編碼後字串如下:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9

payload

載荷即訊息體,這裡會存放實際的內容,也就是Token的資料宣告,例如使用者的idname,預設情況下也會攜帶令牌的簽發時間iat,通過還可以設定過期時間,如下:

{
"sub":"1234567890",
"name":"JohnDoe",
"iat":1516239022
}

同樣進行Base64編碼後,字串如下:

eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ

Signature

簽名是對頭部和載荷內容進行簽名,一般情況,設定一個secretKey,對前兩個的結果進行HMACSHA25演算法,公式如下:

Signature=HMACSHA256(base64Url(header)+.+base64Url(payload),secretKey)

一旦前面兩部分資料被篡改,只要伺服器加密用的金鑰沒有洩露,得到的簽名肯定和之前的簽名不一致

二、如何實現

Token的使用分成了兩部分:

  • 生成token:登入成功的時候,頒發token
  • 驗證token:訪問某些資源或者介面時,驗證token

生成 token

藉助第三方庫jsonwebtoken,通過jsonwebtokensign方法生成一個token

  • 第一個引數指的是 Payload

  • 第二個是祕鑰,服務端特有

  • 第三個引數是 option,可以定義 token 過期時間

constcrypto=require("crypto"),
jwt=require("jsonwebtoken");
//TODO:使用資料庫
//這裡應該是用資料庫儲存,這裡只是演示用
letuserList=[];

classUserController{
//使用者登入
staticasynclogin(ctx){
constdata=ctx.request.body;
if(!data.name||!data.password){
returnctx.body={
code:"000002",
message:"引數不合法"
}
}
constresult=userList.find(item=>item.name===data.name&&item.password===crypto.createHash('md5').update(data.password).digest('hex'))
if(result){
//生成token
consttoken=jwt.sign(
{
name:result.name
},
"test_token",//secret
{expiresIn:60*60}//過期時間:60* 60 s
);
returnctx.body={
code:"0",
message:"登入成功",
data:{
token
}
};
}else{
returnctx.body={
code:"000002",
message:"使用者名稱或密碼錯誤"
};
}
}
}

module.exports=UserController;

在前端接收到token後,一般情況會通過localStorage進行快取,然後將token放到HTTP請求頭Authorization中,關於Authorization的設定,前面要加上 Bearer ,注意後面帶有空格

axios.interceptors.request.use(config=>{
consttoken=localStorage.getItem('token');
config.headers.common['Authorization']='Bearer'+token;//留意這裡的Authorization
returnconfig;
})

校驗token

使用koa-jwt中介軟體進行驗證,方式比較簡單

/注意:放在路由前面
app.use(koajwt({
secret:'test_token'
}).unless({//配置白名單
path:[/\/api\/register/,/\/api\/login/]
}))
  • secret 必須和 sign 時候保持一致
  • 可以通過 unless 配置介面白名單,也就是哪些 URL 可以不用經過校驗,像登陸/註冊都可以不用校驗
  • 校驗的中介軟體需要放在需要校驗的路由前面,無法對前面的 URL 進行校驗

獲取token使用者的資訊方法如下:

router.get('/api/userInfo',async(ctx,next)=>{
constauthorization=ctx.header.authorization//獲取jwt
consttoken=authorization.replace('Beraer','')
constresult=jwt.verify(token,'test_token')
ctx.body=result

注意:上述的HMA256加密演算法為單祕鑰的形式,一旦洩露後果非常的危險

在分散式系統中,每個子系統都要獲取到祕鑰,那麼這個子系統根據該祕鑰可以釋出和驗證令牌,但有些伺服器只需要驗證令牌

這時候可以採用非對稱加密,利用私鑰釋出令牌,公鑰驗證令牌,加密演算法可以選擇RS256

三、優缺點

優點:

  • json具有通用性,所以可以跨語言
  • 組成簡單,位元組佔用小,便於傳輸
  • 服務端無需儲存會話資訊,很容易進行水平擴充套件
  • 一處生成,多處使用,可以在分散式系統中,解決單點登入問題
  • 可防護CSRF攻擊

缺點:

  • payload部分僅僅是進行簡單編碼,所以只能用於儲存邏輯必需的非敏感資訊
  • 需要保護好加密金鑰,一旦洩露後果不堪設想
  • 為避免token被劫持,最好使用https協議

參考文獻

  • http://www.ruanyifeng.com/blog/2018/07/json_web_token-tutorial.html
  • https://blog.wangjunfeng.com/post/golang-jwt/
  • https://vue3js.cn/interview/