node 生成 jwt令牌 并进行API权限分级

1,540 阅读3分钟
jwt是一个无状态的token 不需要我们再把token 存到redis中,它支持自定义参数生成token,然后 客 户 端发送过来 我们用对应的参数去解析校验,但是 弊端是 这样子就不太搞实现单点登录了 所以根据大家的业务需求选择吧。
安装:npm i jsonwebtoken --save

const jwt = require('jsonwebtoken');
const config = require('../config/config');


/**
 * 生成jwt token
 * @param {*} uid 
 * @param {*} scope 
 */
const generateToken = function(uid,scope){
    const key = config.tokenKey.key;
    const expiresIn = config.tokenKey.expiresIn;
    const token = jwt.sign({uid,scope},key,{expiresIn:expiresIn});
    return token
}

module.exports = {
    generateToken
}

我们新建一个工具文件 来存放我们的这个方法:

jwt 使用 sign方法生成参数第一个参数支持我们传入一个js对象:用户id和scope,scope呢 我们暂时传入一个指定数字 2,具体作用 我们在文章《api权限分级》中再去说,第二个参数是一个key,是我们指定的一串字符串,作为密匙,第三个参数就是设置我们的过期时间了,
key和过期时间呢 我是写在了配置文件当中

const config = {
    db:{
        database:'**',
        host:'**',
        port:3306,
        user:'root',
        password:'**'
    },
    redis:{
        port:6379,
        host:'127.0.0.1'
    },
    tokenKey:{
        key:'**',
        expiresIn:60*60*24    //测试环境 所以设置的时间长一点
    }
}
module.exports = config

如何校验token

const jwt = require('jsonwebtoken');
const config = require('../config/config');

class Auth{
    constructor(level){
        this.level = level || 0;
    }
    get w(){
        return async (ctx,next)=>{
            let token = ctx.request.header.token;
            let userId = ctx.request.header.uid;
            if(!token || !userId){
                throw new global.err.HttpException(500,'缺少用户身份信息',403);
            }
            try {
              var becode = jwt.verify(token,config.tokenKey.key)
            } catch (error) {
                console.log(error)
                if(error.name == 'TokenExpiredError'){
                    throw new global.err.HttpException(500,'token已过期,请重新登录',403)
                }
                throw new global.err.HttpException(500,'token不合法',403)
            }
            //验证token和id是否匹配
            if(becode.uid != userId){
                throw new global.err.HttpException(500,'用户id和token不匹配',403)
            }

            //校验权限等级
            if(becode.scope < this.level){
                throw new global.err.HttpException(500,'权限不足',403)
            }
            //把uid,scope 存在放在 ctx中 jwt.verify的时候会返回信息
            ctx.auth = {
                uid:becode.uid,
                scope:becode.scope
            }

            await next();
        }
    }
}

module.exports = {Auth}

API权限分级
在这里的我创建了一个auth类 以中间件的形式去校验token
在我们用jwt生成token时候 ,传入了一个scope参数 这个参数就是我们用来做api 权限分级的
generateToken方法就是我们用来生成token的方法 的时候传入了userType
然后比较this.level和 becode.scope 进行比较 

let token = generateToken(user.id,user.userType)

注意 中间件写完了时候 一定要写 next() 否则其他代码 无法继续向下执行
接下来就是 在我们需要校验api的地方去使用这个类
new Auth(2).w 在实例化类的时候 穿入等级数据 那么用户token里的scope也就是 用户的userType<2的话 就无法访问当前api

/**
 * 根据用户id获取用户信息
 * userId
 */
router.get('/userInfo',new Auth().w, async (ctx, next) => {
  if(!ctx.query.userId){
    throw new global.err.HttpException(500,'缺少参数',500)
  }
  let data = await User.findOne({
    where: { id: ctx.query.userId },
    attributes:{exclude:['registerTime','password']}
  })
  ctx.body = {
    code: 200,
    data: data
  }
})

注意 我们要在中间件的前面去 实例化我们的类 new Auth().w w是个属性 不是方法 注意不要带括号