今天来说说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
}
}
}