Koa從零搭建之實現JWT用戶認證

環境準備

依賴的選擇及安裝

基於上節課講的一些必要依賴之外,咱們須要安裝一下依賴:html

實現登錄接口

咱們依次建立,並實現基礎功能web

  • router
  • controller
  • service
若有忘記,請參閱 Koa從零搭建到Api實現—項目的搭建

密碼加密

通常在涉及到密碼等敏感信息存儲數據庫時,不可能明文存儲,必須對其進行加密。而咱們最常使用的爲md5加密
簡單的密碼md5加密後能夠破解,可是稍微複雜一點的不可破解。故仍是很安全的算法

如何使用

使用很簡單數據庫

const md5 = require('md5')
md5(password)

一般咱們在用戶註冊時,獲取用戶錄入的密碼,加密後存到數據庫。而在登陸時,先對密碼加密,而後再與數據庫中加密後的數據進行匹配。json

Token

Token的組成

  • Header - 頭部
  • Playload - 有效載荷
  • Signature - 簽名

Header

{ 
    「typ」: 「JWT」, 
    「alg」: 「HS256」 
}

由上可知,該token使用HS256加密算法,將頭部使用Base64編碼可獲得一串字符串安全

eyJhbGciOiJIUzI1NiJ91服務器

Playload

{  
  「iss」: 「Online JWT Builder」,  
  「iat」: 1416797419,  
  「exp」: 1448333419,  
      ……. 
  「userid」:10001 
}

有效載荷中存放了token的簽發者(iss)、簽發時間(iat)、過時時間(exp)等以及一些咱們須要寫進token中的信息。有效載荷也使用Base64編碼獲得一串字符串app

eyJ1c2VyaWQiOjB91async

Signature

將Header和Playload拼接生成一個字符串str=「eyJhbGciOiJIUzI1NiJ9.eyJ1c2VyaWQiOjB9」,使用HS256算法和咱們提供的密鑰(secret,服務器本身提供的一個字符串)對str進行加密生成最終的JWT,即咱們須要的令牌(token),形如:str.」簽名字符串」。ui

Token 在服務端與客戶端的交互過程

  1. 客戶端經過用戶名密碼登錄
  2. 服務器驗證用戶名密碼,若經過,生成token返回客戶端
  3. 客戶端收到token後每次請求時攜帶該token(至關於一個令牌,表示我有權限訪問)
  4. 服務器接收token,驗證該token的合法性,若經過驗證,則經過請求,反之,返回請求失敗。

如何驗證Token是否合法

由上文所知,token是咱們根據Base64編碼生成的,反之咱們對其進行解析,解析失敗即token不合法

實戰

登錄接口的實現:

const md5 = require('md5')
const jwt = require('jsonwebtoken')
const secret = require('../config/secret.json')
const login = async (obj) => {
  try {
    if (!obj.name || !obj.password) {
      return  {
        err: '帳號和密碼不能爲空',
        success: false
      }
    } else {
      const name = await db.user.findAll({where: {name: obj.name}})
      // 密碼加密
      obj.password = md5(obj.password)
      // 判斷用戶是否存在
      if (name.length <= 0) {
        return {success: false, err: '當前用戶不存在'}
      } else {
        let results = await db.user.findOne({where: obj})
        // 驗證用戶密碼
        if (results) {
          // 寫入token的信息
          const userToken = {
            name: results.name,
            id: results.id
          }
          // 簽發Token 1小時後過時
          const token = jwt.sign(userToken, secret.sign, {expiresIn: '1h'}) 
          return  {
            success: true,
            token: token,
            data: results
          }
        } else {
          return  {
            success: false,
            err: '密碼錯誤'
          }
        }
      }
    }
  } catch (e) {
    console.log(e)
    return  {
      success: false,
      err: '登陸失敗,請聯繫管理員...'
    }
  }
}
module.exports = {
  login
}

咱們在接口中生成token,將token返回客戶端,客戶端應在請求時攜帶此token,服務端根據token驗證其身份。

在Koa中,咱們能夠經過中間件的方式來實現請求攔截。

Token請求攔截中間件實現

const jwt = require('jsonwebtoken')
const secret = require('../config/secret.json')
const util = require('util')
const verify = util.promisify(jwt.verify)

module.exports = function () {
  return async function (ctx, next) {
    // 設置接口白名單,不進行token驗證
    if (ctx.url === '/user/login') {
      await next()
    } else {
      try {
        let token = ctx.header.authorization  // 獲取請求攜帶的token
        if (token) {
          let payload
          try {
            payload = await verify(token.split(' ')[1].replace(/\s/g,''), secret.sign)  // 解密payload,獲取用戶名和ID
            // 驗證 合法性
            // if (payload.exp <= new Date()/1000) {
            //   console.log('過時了')
            // }
            await next()
          } catch (err) {
            ctx.body = {success: true, message: '登陸密鑰失效或過時,請從新登陸', code: -1}
            console.log('token verify fail: ', err)
          }
        } else {
          ctx.body = {success: false, message: '驗證失敗', code: -1}
        }
      } catch (err) {
        console.log(err)
      }
    }
  }
}

最後將中間件進行引入使用

必定在router以前
app.use(token())
app.use(router.routes())
相關文章
相關標籤/搜索