轻松解决JWT鉴权并实现简单权限管理

1,227 阅读3分钟

JWT介绍

JWT全称JSON Web Token,就是登录成功后将相关用户信息组成 JSON 对象,然后对这个对象进行某种方式的加密,返回给客户端; 客户端在下次请求时带上这个 Token; 服务端再收到请求时校验 token 合法性,其实也就是在校验请求的合法性。

流程图解

jwt.png

代码实践

技术栈

  • 前端:Vue2
  • 后端:Koa + MongoDB

后端部分

1.安装npm i jsonwebtoken,在一个文件中配置

const jwt = require('jsonwebtoken')
const { secretKey, expiresIn } = require('../config/config')

// ../config/config中的内容
module.exports = {
  secretKey: 'chrisWuHaHaHa', //密钥,用来解密token
  expiresIn: 24 * 60 * 60 //过期时间
}

// 生成token
function generateToken(uid, scope,role) {
//第一个参数是保留在token的信息,可以用来鉴权
  const token = jwt.sign(
    {
      uid,
      role //user的角色权限
    },
    secretKey,
    {
      expiresIn
    }
  )

  return token
}

module.exports = {
  generateToken
}

2.在登录接口中生成token并返回给前端,user.role有两个角色admin和student

// 检测用户名、密码,返回 token 接口
tokenRouter.post('/', async ctx => {
  // console.log(ctx.request);
  const { username, password } = ctx.request.body;
  let user = await findUser(username)
  if (!user) {
    ctx.body = {
      errCode: 10001,
      msg: '用户名或密码不正确',
    }
    return
  }

  const token = generateToken(user.id,user.role)
  user.token = `Bearer ${token}`

  ctx.body = {
    user,token:`Bearer ${token}`
  }
})

3.在需要登录下的请求,接口中可以jwt.verify利用判断

app.use(async (ctx, next) => {  
    const token = ctx.request.header.authorization.split(' ')[1];
    const isValid = jwt.verify(token, secretKey);
    if(isValid){
      ......
    }else{
      ctx.status = 401;
      ctx.body = '登录已失效';
    }
  }
})

这里的isValid打印出来是

isValid {
  uid: '6373a897b4edc7143c098ee2',
  role: 'admin',
  iat: 1668865922,
  exp: 1668952322
}

到这一步JWT的鉴权已初步完成

但是设想一下JWT中的expiresIn只设置了10秒,用户每一次操作的间隔会很长,再想操作页面时cookie已过期,那又需要重新登录,这样体验感会很差😅😅

4.所以我们可以设置一个全局的中间件,在每次请求时更新token,重新发给前端

这个中间件需要放在路由前

app.use(async (ctx, next) => {  
  if(ctx.url.includes('token')){
  //登录接口直接放行
    await next();
  }else{
    // 前端在请求header.authorization中带上token
    const token = ctx.request.header.authorization.split(' ')[1];
    const isValid = jwt.verify(token, secretKey);
    if(isValid){
    // 生成新的token
      const newToken = generateToken(isValid.uid,
        isValid.role)
    // 发给前端   
      ctx.set('Authorization',`Bearer ${newToken}`)
      await next();
    }else{
      ctx.status = 401;
      ctx.body = '登录已失效';
    }
  }
})

5.权限判断

一些接口需要admin权限才可以访问,所以我们可以写一个类,里面定义一个中间件判断权限

const jwt = require('jsonwebtoken')
const { secretKey } = require('../config/config')
class Auth {
  get middleware() {
    return async (ctx, next) => {
      const token = ctx.request.header.authorization.split(' ')[1];
      // 解密出token中的保留的信息
      let result = jwt.verify(token, secretKey)
      if (result.role === 'student') {
        ctx.body = {
          errCode: 1005,
          msg: '权限不足',
        }
        return
      }
      await next()
    }
  }
}

module.exports = Auth

在接口中使用

contentRouter.post('/', new Auth().middleware, async ctx => {
  ctx.body = '新增内容成功'
})

在这一步后端工作已全部完成了

前端部分

1.登录,将token储存到并存储到localStorage中

let result = await login(this.form);
// 拿到后端的token,并存储到localStorage中
localStorage.setItem('token', result.data.token);

2.axios的封装

//拦截请求
request.interceptors.request.use((config)=>{
  let token =  localStorage.getItem('token');
  // 每次在请求头上带上token,登录接口时token是null,后端已放权
  config.headers.Authorization = token;
  return config
})
//拦截响应
request.interceptors.response.use((response)=>{
// 后端每次更新token会放在响应头中,所有重新保存在localStorage中
  if(response.headers.authorization){
    localStorage.setItem('token', response.headers.authorization)
  }

  return response
},function (error){
  //对响应的错误做点什么
  // console.log(error);
  // 如果token过期,后端返回401状态码
  if(error.response.status == 401){
    router.push('/login')
  }
  // console.log(error.response)

  return Promise.reject(error);
}
)

这一步前端的工作也完成了

END

  • 整个前后端JWT鉴权+简单权限管理就全部实现了,相信看完session-cookie和jwt这两篇文章后,再也不怕面试官问到登录问题哈哈哈😃,接下来一篇打算后台权限管理的实现
  • 希望这篇文章可以帮助到有需要的小伙伴,有问题可以在评论区留言或私信我

一文教你搞定所有前端鉴权与后端鉴权方案