1. 程式人生 > >JSON Web Token實戰篇——基於koa開發WEB後臺認證機制

JSON Web Token實戰篇——基於koa開發WEB後臺認證機制

今天來說說JSON Web Token,JSON Web Token(縮寫 JWT)是目前最流行的跨域認證解決方案。關於它的介紹,可以看阮一峰的這篇文章JSON Web Token 入門教程

工作流程
這裡直接使用官網的圖
在這裡插入圖片描述
知道了JWT的原理後,我們來看一下如何基於koa開發web後臺認證機制:
這裡,我們使用了jsonwebtoken模組,需要在專案中新增jsonwebtoken模組

npm install koa-jwt

關於jsonwebtoken模組的用法,可以參照這篇文章jsonwebtoken中文文件

程式碼實現

這裡通過koa來建立一個server

1、web端,發起登入請求,將返回的token儲存在session中

//發起登入請求
let reqObj={
  username: val.username,
  password:password
}
const result = this.$http.post('/phoneManageSystem/login/login', reqObj) // 將資訊傳送給後端
result.then((res) => {
  if (res.data.returnCode == '000000') {
	sessionStorage.setItem('login-token', res.data.
result.token) // 用sessionStorage把token存下來 } else { sessionStorage.setItem('login-token', null) // 將token清空 } }, (err) => { this.$message.error('請求錯誤!') sessionStorage.setItem('login-token', null) // 將token清空 })

2、koa端,登入成功後簽發token

//koa端
//登入成功後簽發token
import jwt from 'jsonwebtoken'
const nameToToken =
async function (ctx) { const data = ctx.request.body try { const userInfo = await user.getUserByName(data.username) if (userInfo != null) { let userToken = { username: userInfo.account, id: userInfo.id, role: userInfo.role } let secret = 'lxPhone' // 指定金鑰 let token = jwt.sign(userToken, secret, { expiresIn: '1h' }) // 簽發token ctx.body = { result: { token: token, name: userInfo.name, role: userInfo.role }, returnCode: "000000", returnMsg: "token獲取成功" } } else { ctx.body = { returnCode: "000002", returnMsg: "使用者名稱不存在" } } } catch (err) { ctx.body = { returnCode: "000001", returnMsg: "服務端錯誤" } } }

3、web端,設定Bearer Token請求頭

//設定Bearer Token請求頭
router.beforeEach((to, from, next) => {
  const token = sessionStorage.getItem('login-token')
  if (to.path === '/') { // 如果是跳轉到登入頁的
    if (token !== 'null' && token !== null) {
      next('/main') // 如果有token就轉向todolist不返回登入頁
    }
    next() // 否則跳轉回登入頁
  } else {
    //store.commit('SET_ROUTER',to.path)
    if (!!token && token !== 'null' && token !== null) {
       axios.defaults.headers.common['Authorization'] = 'Bearer ' + token // 注意Bearer後有個空格
      next() // 如果有token就正常轉向
    } else {
      next('/') // 否則跳轉回登入頁
    }
  }
})

這裡對請求頭的配置進行一下介紹

首部欄位Authorization是用來告知伺服器,使用者代理的認證資訊(證書值)。通常,想要通過伺服器認證的使用者代理會在接收到返回的401狀態碼響應後,把首部欄位Authorization加入請求中。共用快取在接收到含有Authorization首部欄位的請求時的操作處理會略有差異。關於Bearer Token的相關定義與使用方法,請看這篇文章關於Bearer Token的相關定義與使用方法

4、編寫token驗證中介軟體

//編寫token驗證中介軟體
const jwt = require('jsonwebtoken');
const util = require('util');
import userModels from '../models/usertest.js'
const verify = util.promisify(jwt.verify);
/**
 * 判斷token是否可用
 */
module.exports = function () {
    return async function (ctx, next) {
        // 獲取jwt
        const token = ctx.header.authorization;
        if (!!token) {
            try {
                // 解密payload,獲取使用者名稱和ID
                let payload = await verify(token.split(' ')[1], 'lxPhone');
                let user = await userModels.getUserByName(payload.username)
                if (!!user) {
                    ctx.user = {
                        username: payload.account,
                        id: payload.id,
                        role: payload.role
                    }
                } else {
                    console.log('解析出來的域賬號不在資料庫中')
                }
            } catch (err) {
                ctx.body = {
                    success: 0,
                    message: '認證失敗',
                    returnCode: '000005'
                };
            }
        }
        await next();
    }
}

5、選擇驗證路由

//選擇驗證路由
import koaRouter from 'koa-router'
import phonelist from './phonelist.js'
import usertest from './usertest.js'
import upload from './upload.js'
import login from './login.js'
import SIM from './SIM.js'
import book from './book.js'
import headset from './headset.js'
import jwt from 'koa-jwt'
const tokenError = require('../api/token');

const router = koaRouter()

router.use('/phoneManageSystem/login', login.routes())
router.use('/phoneManageSystem/upload', upload.routes())
router.use('/phoneManageSystem/user', jwt({ secret: 'lxPhone' }), usertest.routes())
router.use('/phoneManageSystem/phonelist', jwt({ secret: 'lxPhone' }), phonelist.routes())
router.use('/phoneManageSystem/SIM', jwt({ secret: 'lxPhone' }), SIM.routes())
router.use('/phoneManageSystem/book', jwt({ secret: 'lxPhone' }), book.routes())
router.use('/phoneManageSystem/headset', jwt({ secret: 'lxPhone' }), headset.routes())

export default router

6、在controller層進行具體許可權驗證

const deletePhone = async function (ctx) {
    const id = ctx.request.body.id
    try {
        if (!!ctx.user && ctx.user.role == '超級管理員' || ctx.user.role == '管理員') {
            const result = await phonelist.deletePhone(id)
            ctx.body = {
                returnCode: "000000",
                returnMsg: "成功"
            }
        } else {
            ctx.body = {
                returnCode: '000005',
                success: false,
                message: 'token認證失敗'
            }
        }
    } catch (error) {
        ctx.body = {
            returnCode: "000001",
            returnMsg: error
        }
    }
}