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,是我们指定的一串字符串,作为密匙,第三个参数就是设置我们的过期时间了,
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
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}
在这里的我创建了一个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是个属性 不是方法 注意不要带括号