JSON Web Token实战篇——基于koa开发WEB后台认证机制

时间:2024-04-03 18:45:11

今天来说说JSON Web Token,JSON Web Token(缩写 JWT)是目前最流行的跨域认证解决方案。关于它的介绍,可以看阮一峰的这篇文章JSON Web Token 入门教程

工作流程
这里直接使用官网的图
JSON Web Token实战篇——基于koa开发WEB后台认证机制
知道了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
        }
    }
}