JSON Web Token實戰篇——基於koa開發WEB後臺認證機制
阿新 • • 發佈:2018-11-05
今天來說說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
}
}
}