上一篇【基于token的持久化登录讲解及其实现】讲到token的机制,以及token的2大特性,即:只有服务器能够签发token,服务器可以验证token是否由自己签发。
可是上一篇博客编写的时候,对token的理解还不够深入,上一篇博客的token是最最最基本的token验证,而现在互联网应用的登录验证普遍使用JWT,即 JSON WEB TOKEN 的规范化验证,所以今天来学习一蛤,并且学习如何在node上编写token的签发与验证程序。
JWT,即 JSON WEB TOKEN ,是一种规范化的无状态登录验证方式。因为以JSON的形式表示,所以JWT天然的不受编程语言的限制。
jwt中,token是一串字符串,由.
号分割为三个部分,下面给出一条完整的token
完整:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoid29ybGRIZWxsbyIsImlkIjoxMTQ1MTQsImlhdCI6MTU5NzM5Nzk0MSwiZXhwIjoxNTk3NjU3MTQxfQ.otN7LikyB1mmknnBXL0mVrdOSFmID-dCORD5x4R3voM
第一部分:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
第二部分
eyJuYW1lIjoid29ybGRIZWxsbyIsImlkIjoxMTQ1MTQsImlhdCI6MTU5NzM5Nzk0MSwiZXhwIjoxNTk3NjU3MTQxfQ
第三部分
otN7LikyB1mmknnBXL0mVrdOSFmID-dCORD5x4R3voM
token的第一和第二部分,都是base64编码的,这意味着他们是明文的,可以被解码的,下面给出各个部分的解码结果
第一部分:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
解码结果:
{"alg":"HS256","typ":"JWT"}
第二部分:
eyJuYW1lIjoid29ybGRIZWxsbyIsImlkIjoxMTQ1MTQsImlhdCI6MTU5NzM5Nzk0MSwiZXhwIjoxNTk3NjU3MTQxfQ
解码结果:
{"name":"worldHello","id":114514,"iat":1597397941,"exp":1597657141}
看到json格式的数据,想必都懂了,为啥叫json web token。
其中,第一部分声明了使用的算法,和token的类型,即为SHA256加密算法的JWT。
第二部分则是一些服务要用到的用户信息,这部分是服务器自定义添加的,比如用户登录的用户名和昵称,用户的余额等。在上诉例子中,我们添加name
和id
字段。值得注意的时,iat和exp字段表示签发时间和过期时间,token验证用。
第三部分是前两部分base64编码串,加上一个密钥,使用SHA256算法加密的结果(使用的算法在第一部分声明,即HS256)。值得注意的是,这个密钥只有服务器有,所以保证token都是服务器签发的。
具体的加密方式如下:
.
号分割)eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoid29ybGRIZWxsbyIsImlkIjoxMTQ1MTQsImlhdCI6MTU5NzM5Nzk0MSwiZXhwIjoxNTk3NjU3MTQxfQ
因为整合了前两部分信息,服务器验证token时,利用前两部分+密钥,再次产生第三部分,然后和用户提交的第三部分比对,从而验证。
在node.js中使用JWT,我们直接用别人造好的轮子就好了,不必自己重复造轮子了。。
使用node的包管理工具npm可以快速安装
npm install jsonwebtoken
如果在linux下,记得加sudo再安装。如果报错,那么尝试使用npm link命令
npm link jsonwebtoken
签发token,我们需要指定两个信息:
使用如下的jwt.sign
函数,我们可以很快签发一个token,我们指定两个用户信息,即name
和id
,然后我们返回这个token。token的有效期是3天,当然你也可以指定 1000 表示有效期为1000毫秒。
var express = require('express');
var app = express();
var jwt = require('jsonwebtoken');
app.post('/', function(req, res, next) {
// 信息部分
var content = {
"name": "worldHello",
"id": 114514
};
// 密钥
var key = "hello_world";
var token = jwt.sign(content, key, {expiresIn:"3 days"});
res.end(token);
});
app.listen(8888);
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoid29ybGRIZWxsbyIsImlkIjoxMTQ1MTQsImlhdCI6MTU5NzQwMDc1OSwiZXhwIjoxNTk3NjU5OTU5fQ.0DFAZtFpN28PbE2wbSzyMv8rwHvl6T5QSGjK3brX_bI
使用 jwt.verify
函数以验证token是否正确,我们传入token和分发token时的密钥(两个密钥须一致),回调函数就会处理
回调返回decode对象,这个对象是token【第二部分】的json字符串转过来的。我们可以读取用户token中的信息,并且回显:
注:这里使用 body-parser 包来进行POST请求参数的获取
var express = require('express');
var app = express();
var bodyParser = require('body-parser');
app.use(bodyParser.urlencoded({ extended: false })); // 解析POST
app.use(bodyParser.json()); // 变成json对象
var jwt = require('jsonwebtoken');
app.post('/', function(req, res, next) {
var token = req.body["token"]; // 获取token
var key = "hello_world"; // 使用同样的密钥
jwt.verify(token, key, function(err, decode) {
// 如果token失效直接返回
if(err) {
res.end(JSON.stringify({
"state": -111,
"message": "token校验失败。信息: " + err.message
}));
return;
}
// 验证成功 返回用户名字
res.end(JSON.stringify({
"state": 1234,
"message": decode.name
}));
});
});
app.listen(8888);
我们填入刚刚的token,尝试访问验证接口,如果正确,那么我们会看到我们token中保存的信息,即name字段:
如果我们填写错误的token,那么会被识别出来,比如我们篡改token的信息: