[JWT][Koa2]Koa2实现token的签发和验证
1、前言
JWT是JSON Web Token的简写,是一种可跨域的身份认证方案。
JWT可以让服务器不再维护session等用户状态,只要token有效,就认为是合法的用户。
但是这样也有一个缺点,就是除非token到期,否则服务器没法主动让token失效。
要解决这个问题,可以把每个用户的token保存在redis数据库,每次在token验证为有效后,还要在redis中查询此token是否存在。这样,既能保证这个查询的速度(redis是内存数据库),也能通过操作redis中的数据,让某些token失效。
JWT的概念参考这篇文章:
JSON Web Token 入门教程:
http://www.ruanyifeng.com/blog/2018/07/json_web_token-tutorial.html
下面是在Koa2中的简单实现。
2、安装依赖
首先安装jsonwebtoken
,这是一个为nodejs开发的jwt工具:
npm install jsonwebtoken --save
jsonwebtoken的GitHub地址:
https://github.com/auth0/node-jsonwebtoken
3、签发token
一般是在注册成功或者登录成功后,会下发token给客户端。
const jwt = require('jsonwebtoken');
...
// 注册,成功后返回用户信息给userInfo
let userInfo = await User.create({
name: ctx.request.body.name,
password: ctx.request.body.password
});
// 注册成功
if(userInfo) {
// 放在token中的数据
let payload = {
userid: userInfo.id,
name: userInfo.name
};
// 密钥,可以是自定的字符串,也可以是私钥
let sign = 'my sign';
// 其他选项
let options = {
//过期时间,表示秒的数字,或者表示时间跨度的字符串,格式见:
// https://github.com/zeit/ms
expiresIn: '2 days'
};
let token = jwt.sign(payload, sign, options);
// 发送给客户端
userInfo.token = token;
ctx.response.status = 200;
ctx.response.type = 'application/json';
ctx.response.body = {
code: 0,
userInfo: userInfo
};
}
生成的token类似这样:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MiwiaWF0IjoxNTc4ODEyNzAzLCJleHAiOjE1Nzk0MTc1MDN9.fkNjC9u_oj-ye50dKTozfbZ2ouWh_HmNU5huVzSTrVQ
可以看到,token分为三部分,通过两个.
连接。
验证token
可以编写一个中间件,判断请求是否需要验证token。如果验证失败,则返回相应的错误:
const jwt = require('jsonwebtoken');
module.exports = () => {
return async (ctx, next) {
// 请求路径为/api/xxx,并且不是/api/login,因为请求登录的时候还没有token
if (ctx.request.path.startsWith('/api/') && !ctx.request.path.endsWith('login')) {
// 假定请求方式全部为POST,并且token在请求头的token字段中
let token = ctx.request.header.token,
userid = ctx.request.body.userid,
name = ctx.request.body.name;
if(!token) {
ctx.response.status = 400;
ctx.response.body = {
code: -1,
message: 'token不存在'
};
return;
}
try {
let payload = jwt.verify(token, 'my sign');
if(payload.userid !== userid || payload.name !== name) {
throw new Error('token无效');
}
await next();
} catch (err) {
ctx.response.status = 400;
ctx.response.body = {
code: -2,
message: 'token无效'
};
}
} else {
await next();
}
};
};
这样,请求/api/xxx
的路径时,Koa就会验证token是否有效,如果无效则返回http 400和错误信息。
End