egg基於jsonwebtoken的Token實現認證機制

因禿而強發表於2018-12-17

Json web token(JWT)是為了網路應用環境間傳遞宣告而執行的一種基於JSON的開發標準(RFC 7519),該token被設計為緊湊且安全的,特別適用於分散式站點的單點登陸(SSO)場景。JWT的宣告一般被用來在身份提供者和服務提供者間傳遞被認證的使用者身份資訊,以便於從資源伺服器獲取資源,也可以增加一些額外的其它業務邏輯所必須的宣告資訊,該token也可直接被用於認證,也可被加密。

相關技術點

eggmiddleware

jwt官網

node-jsonwebtoken

安裝

npm install jsonwebtoken
複製程式碼

Middleware中介軟體編寫

在middleware檔案下新建一個jwt.js檔案

'use strict'
const fs = require('fs')
const path = require('path')
const jwt = require('jsonwebtoken') //引入jsonwebtoken

module.exports = (options, app) => {
  return async function userInterceptor(ctx, next) {
    let authToken = ctx.header.authorization // 獲取header裡的authorization
    if (authToken) {
      authToken = authToken.substring(7)
      const res = verifyToken(authToken) // 解密獲取的Token
      if (res.corpid && res.userid) {
        // 如果需要限制單端登陸或者使用過程中廢止某個token,或者更改token的許可權。也就是說,一旦 JWT 簽發了,在到期之前就會始終有效
        // 此處使用redis進行儲存
        const redis_token = await app.redis.get('loginToken').get(res.corpid + res.userid) // 獲取儲存的token
        if (authToken === redis_token) {
          ctx.locals.corpid = res.corpid
          ctx.locals.userid = res.userid
          await next()
        } else {
          ctx.body = { code: 50012, msg: '您的賬號已在其他地方登入' }
        }
      } else {
        ctx.body = { code: 50012, msg: '登入狀態已過期' }
      }
    } else {
      ctx.body = { code: 50008, msg: '請登陸後再進行操作' }
    }
  }
}

// 解密,驗證
function verifyToken(token) {
  const cert = fs.readFileSync(path.join(__dirname, '../public/rsa_public_key.pem')) // 公鑰,看後面生成方法
  let res = ''
  try {
    const result = jwt.verify(token, cert, { algorithms: [ 'RS256' ] }) || {}
    const { exp } = result,
      current = Math.floor(Date.now() / 1000)
    if (current <= exp) res = result.data || {}
  } catch (e) {
    console.log(e)
  }
  return res
}
複製程式碼

使用中介軟體

中加入下面的配置就完成了中介軟體的開啟和配置

// 方法一:在應用中使用中介軟體
config.middleware = [ 'jwt' ]

config.jwt = {
	enable: true,
	ignore: [ '/api/v1/test/', '/public/' ], // 哪些請求不需要認證
}

// 方法二:router中使用中介軟體
module.exports = app => {
  const jwt = app.middleware.jwt();
  app.router.get('/api/v1/test/', jwt, app.controller.test.test);
};
複製程式碼

token生成

建議寫在裡,方便呼叫

loginToken(data, expires = 7200) {
  const exp = Math.floor(Date.now() / 1000) + expires
  const cert = fs.readFileSync(path.join(__dirname, '../public/rsa_private_key.pem')) // 私鑰,看後面生成方法
  const token = jwt.sign({ data, exp }, cert, { algorithm: 'RS256' })
  return token
}
複製程式碼

呼叫token生成方法

const token = ctx.helper.loginToken({ corpid: usersData.corpid, userid: usersData.userid }, 7200) // token生成
await app.redis.get('loginToken').set(usersData.corpid + usersData.userid, token, 'ex', 7200) // 儲存到redis
ctx.body = { data: { token, expires: this.config.login_token_time }, code: 1, msg: '登入成功' } // 返回前端
複製程式碼

前端使用

設定headers的Authorization = 'Bearer ' + Token

例:axios中
// request攔截器
service.interceptors.request.use(config => {
  if (store.getters.token) {
    config.headers['Authorization'] = `Bearer ${getToken()}`
  }
  return config
}, error => {
  console.log(error)
  Promise.reject(error)
})
複製程式碼

利用Openssl生成私鑰公鑰

此處介紹window下已安裝git的方法 設定環境變數 我的電腦 > 右鍵 > 屬性 > 高階系統設定 > 環境變數 > path > 編輯 > 新建git路徑\bingit路徑\usr\bin 如:C:\Program Files\Git\binC:\Program Files\Git\usr\bin 生成公鑰:openssl genrsa -out rsa_private_key.pem 1024 生成私鑰: openssl rsa -in rsa_private_key.pem -pubout -out rsa_public_key.pem

END

歡迎關注我的部落格

相關文章