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

标签: nodejs, jwt, koa2